def set_class_name(self, name): if not isinstance(name, str): raise CX(_("class name must be a string")) for x in name: if not x.isalnum() and x not in ["_", "-", ".", ":", "+"]: raise CX(_("invalid characters in class name: '%s'" % name)) self.class_name = name
def set_parent(self, parent_name): """ Instead of a --distro, set the parent of this object to another profile and use the values from the parent instead of this one where the values for this profile aren't filled in, and blend them together where they are dictionaries. Basically this enables profile inheritance. To use this, the object MUST have been constructed with is_subobject=True or the default values for everything will be screwed up and this will likely NOT work. So, API users -- make sure you pass is_subobject=True into the constructor when using this. """ old_parent = self.get_parent() if isinstance(old_parent, item.Item): old_parent.children.pop(self.name, 'pass') if parent_name is None or parent_name == '': self.parent = '' return if parent_name == self.name: # check must be done in two places as set_parent could be called before/after # set_name... raise CX(_("self parentage is weird")) found = self.collection_mgr.profiles().find(name=parent_name) if found is None: raise CX(_("profile %s not found, inheritance not possible") % parent_name) self.parent = parent_name self.depth = found.depth + 1 parent = self.get_parent() if isinstance(parent, item.Item): parent.children[self.name] = self
def __get_interface(self, name): if name == "" and len(self.interfaces.keys()) == 0: raise CX(_("No interfaces defined. Please use --interface <interface_name>")) elif name == "" and len(self.interfaces.keys()) == 1: name = self.interfaces.keys()[0] elif name == "" and len(self.interfaces.keys()) > 1: raise CX(_("Multiple interfaces defined. Please use --interface <interface_name>")) elif name not in self.interfaces: self.interfaces[name] = { "mac_address": "", "mtu": "", "ip_address": "", "dhcp_tag": "", "netmask": "", "if_gateway": "", "virt_bridge": "", "static": False, "interface_type": "", "interface_master": "", "bonding_opts": "", "bridge_opts": "", "management": False, "dns_name": "", "static_routes": [], "ipv6_address": "", "ipv6_prefix": "", "ipv6_secondaries": [], "ipv6_mtu": "", "ipv6_static_routes": [], "ipv6_default_gateway": "", "cnames": [], } return self.interfaces[name]
def auto_add_repos(self): """ Import any repos this server knows about and mirror them. Credit: Seth Vidal. """ self.log("auto_add_repos") try: import yum except: raise CX(_("yum is not installed")) version = yum.__version__ (a, b, c) = version.split(".") version = a * 1000 + b * 100 + c if version < 324: raise CX(_("need yum > 3.2.4 to proceed")) base = yum.YumBase() base.doRepoSetup() repos = base.repos.listEnabled() if len(repos) == 0: raise CX(_("no repos enabled/available -- giving up.")) for repo in repos: url = repo.urls[0] cobbler_repo = self.new_repo() auto_name = repo.name.replace(" ", "") # FIXME: probably doesn't work for yum-rhn-plugin ATM cobbler_repo.set_mirror(url) cobbler_repo.set_name(auto_name) print "auto adding: %s (%s)" % (auto_name, url) self._collection_mgr.repos().add(cobbler_repo, save=True)
def __duplication_checks(self, ref, check_for_duplicate_names, check_for_duplicate_netinfo): """ Prevents adding objects with the same name. Prevents adding or editing to provide the same IP, or MAC. Enforcement is based on whether the API caller requests it. """ # always protect against duplicate names if check_for_duplicate_names: match = None if isinstance(ref, item_system.System): match = self.api.find_system(ref.name) elif isinstance(ref, item_profile.Profile): match = self.api.find_profile(ref.name) elif isinstance(ref, item_distro.Distro): match = self.api.find_distro(ref.name) elif isinstance(ref, item_repo.Repo): match = self.api.find_repo(ref.name) elif isinstance(ref, item_image.Image): match = self.api.find_image(ref.name) elif isinstance(ref, item_mgmtclass.Mgmtclass): match = self.api.find_mgmtclass(ref.name) else: raise CX("internal error, unknown object type") if match: raise CX(_("An object already exists with that name. Try 'edit'?")) # the duplicate mac/ip checks can be disabled. if not check_for_duplicate_netinfo: return if isinstance(ref, item_system.System): for (name, intf) in ref.interfaces.iteritems(): match_ip = [] match_mac = [] match_hosts = [] input_mac = intf["mac_address"] input_ip = intf["ip_address"] input_dns = intf["dns_name"] if not self.api.settings().allow_duplicate_macs and input_mac is not None and input_mac != "": match_mac = self.api.find_system(mac_address=input_mac, return_list=True) if not self.api.settings().allow_duplicate_ips and input_ip is not None and input_ip != "": match_ip = self.api.find_system(ip_address=input_ip, return_list=True) # it's ok to conflict with your own net info. if not self.api.settings().allow_duplicate_hostnames and input_dns is not None and input_dns != "": match_hosts = self.api.find_system(dns_name=input_dns, return_list=True) for x in match_mac: if x.name != ref.name: raise CX(_("Can't save system %s. The MAC address (%s) is already used by system %s (%s)") % (ref.name, intf["mac_address"], x.name, name)) for x in match_ip: if x.name != ref.name: raise CX(_("Can't save system %s. The IP address (%s) is already used by system %s (%s)") % (ref.name, intf["ip_address"], x.name, name)) for x in match_hosts: if x.name != ref.name: raise CX(_("Can't save system %s. The dns name (%s) is already used by system %s (%s)") % (ref.name, intf["dns_name"], x.name, name))
def rename_interface(self, names): """ Used to rename an interface. """ (name, newname) = names if name not in self.interfaces: raise CX(_("Interface %s does not exist" % name)) if newname in self.interfaces: raise CX(_("Interface %s already exists" % newname)) else: self.interfaces[newname] = self.interfaces[name] del self.interfaces[name]
def remove(self, name, with_delete=True, with_sync=True, with_triggers=True, recursive=True, logger=None): """ Remove element named 'name' from the collection """ # NOTE: with_delete isn't currently meaningful for repos # but is left in for consistancy in the API. Unused. name = name.lower() # first see if any Groups use this distro if not recursive: for v in self.config.systems(): if v.image is not None and v.image.lower() == name: raise CX(_("removal would orphan system: %s") % v.name) obj = self.find(name=name) if obj is not None: if recursive: kids = obj.get_children() for k in kids: self.config.api.remove_system(k, recursive=True, logger=logger) if with_delete: if with_triggers: utils.run_triggers(self.config.api, obj, "/var/lib/cobbler/triggers/delete/image/pre/*", [], logger) if with_sync: lite_sync = action_litesync.BootLiteSync(self.config, logger=logger) lite_sync.remove_single_image(name) self.lock.acquire() try: del self.listing[name] finally: self.lock.release() self.config.serialize_delete(self, obj) if with_delete: if with_triggers: utils.run_triggers( self.config.api, obj, "/var/lib/cobbler/triggers/delete/image/post/*", [], logger ) utils.run_triggers(self.config.api, obj, "/var/lib/cobbler/triggers/change/*", [], logger) return True raise CX(_("cannot delete an object that does not exist: %s") % name)
def remove(self, name, with_delete=True, with_sync=True, with_triggers=True, recursive=False, logger=None): """ Remove element named 'name' from the collection """ # NOTE: with_delete isn't currently meaningful for repos # but is left in for consistancy in the API. Unused. name = name.lower() obj = self.find(name=name) if obj is not None: if with_delete: if with_triggers: utils.run_triggers(self.collection_mgr.api, obj, "/var/lib/cobbler/triggers/delete/repo/pre/*", [], logger) self.lock.acquire() try: del self.listing[name] finally: self.lock.release() self.collection_mgr.serialize_delete(self, obj) if with_delete: if with_triggers: utils.run_triggers(self.collection_mgr.api, obj, "/var/lib/cobbler/triggers/delete/repo/post/*", [], logger) utils.run_triggers(self.collection_mgr.api, obj, "/var/lib/cobbler/triggers/change/*", [], logger) # FIXME: better use config.settings() webdir? path = "/var/www/cobbler/repo_mirror/%s" % obj.name if os.path.exists("/srv/www/"): path = "/srv/www/cobbler/repo_mirror/%s" % obj.name if os.path.exists(path): utils.rmtree(path) return raise CX(_("cannot delete an object that does not exist: %s") % name)
def remove(self, name, with_delete=True, with_sync=True, with_triggers=True, recursive=False, logger=None): """ Remove element named 'name' from the collection """ name = name.lower() obj = self.find(name=name) if obj is not None: if with_delete: if with_triggers: utils.run_triggers(self.collection_mgr.api, obj, "/var/lib/cobbler/triggers/delete/file/*", [], logger) self.lock.acquire() try: del self.listing[name] finally: self.lock.release() self.collection_mgr.serialize_delete(self, obj) if with_delete: if with_triggers: utils.run_triggers(self.collection_mgr.api, obj, "/var/lib/cobbler/triggers/delete/file/post/*", [], logger) utils.run_triggers(self.collection_mgr.api, obj, "/var/lib/cobbler/triggers/change/*", [], logger) return raise CX(_("cannot delete an object that does not exist: %s") % name)
def set_profile(self, profile_name): """ Set the system to use a certain named profile. The profile must have already been loaded into the Profiles collection. """ old_parent = self.get_parent() if profile_name in ["delete", "None", "~", ""] or profile_name is None: self.profile = "" if isinstance(old_parent, item.Item): old_parent.children.pop(self.name, 'pass') return True self.image = "" # mutual exclusion rule p = self.config.profiles().find(name=profile_name) if p is not None: self.profile = profile_name self.depth = p.depth + 1 # subprofiles have varying depths. if isinstance(old_parent, item.Item): old_parent.children.pop(self.name, 'pass') new_parent = self.get_parent() if isinstance(new_parent, item.Item): new_parent.children[self.name] = self return True raise CX(_("invalid profile name: %s") % profile_name)
def set_image(self, image_name): """ Set the system to use a certain named image. Works like set_profile but cannot be used at the same time. It's one or the other. """ old_parent = self.get_parent() if image_name in ["delete", "None", "~", ""] or image_name is None: self.image = "" if isinstance(old_parent, item.Item): old_parent.children.pop(self.name, 'pass') return True self.profile = "" # mutual exclusion rule img = self.config.images().find(name=image_name) if img is not None: self.image = image_name self.depth = img.depth + 1 if isinstance(old_parent, item.Item): old_parent.children.pop(self.name, 'pass') new_parent = self.get_parent() if isinstance(new_parent, item.Item): new_parent.children[self.name] = self return True raise CX(_("invalid image name (%s)") % image_name)
def set_file(self, filename): """ Stores the image location. This should be accessible on all nodes that need to access it. Format: can be one of the following: * username:password@hostname:/path/to/the/filename.ext * username@hostname:/path/to/the/filename.ext * hostname:/path/to/the/filename.ext * /path/to/the/filename.ext """ uri = "" scheme = auth = hostname = path = "" # we'll discard the protocol if it's supplied, for legacy support if filename.find("://") != -1: scheme, uri = filename.split("://") filename = uri else: uri = filename if filename.find("@") != -1: auth, filename = filename.split("@") # extract the hostname # 1. if we have a colon, then everything before it is a hostname # 2. if we don't have a colon, then check if we had a scheme; if # we did, then grab all before the first forward slash as the # hostname; otherwise, we've got a bad file if filename.find(":") != -1: hostname, filename = filename.split(":") elif filename[0] != '/': if len(scheme) > 0: index = filename.find("/") hostname = filename[:index] filename = filename[index:] else: raise CX(_("invalid file: %s" % filename)) # raise an exception if we don't have a valid path if len(filename) > 0 and filename[0] != '/': raise CX(_("file contains an invalid path: %s" % filename)) if filename.find("/") != -1: path, filename = filename.rsplit("/", 1) if len(filename) == 0: raise CX(_("missing filename")) if len(auth) > 0 and len(hostname) == 0: raise CX(_("a hostname must be specified with authentication details")) self.file = uri return True
def set_interface_type(self, type, interface): interface_types = ["bridge", "bridge_slave", "bond", "bond_slave", "bonded_bridge_slave", "bmc", "na", ""] if type not in interface_types: raise CX(_("interface type value must be one of: %s or blank" % ",".join(interface_types))) if type == "na": type = "" intf = self.__get_interface(interface) intf["interface_type"] = type
def __get_interface(self, name): if not name: raise CX(_("No network interface name provided")) if name not in self.interfaces: self.__create_interface(name) return self.interfaces[name]
def __write_named_conf(self): """ Write out the named.conf main config file from the template. """ settings_file = self.settings.bind_chroot_path + self.settings_file template_file = "/etc/cobbler/named.template" # forward_zones = self.settings.manage_forward_zones # reverse_zones = self.settings.manage_reverse_zones metadata = {'forward_zones': self.__forward_zones().keys(), 'reverse_zones': [], 'zone_include': ''} for zone in metadata['forward_zones']: txt = """ zone "%(zone)s." { type master; file "%(zone)s"; }; """ % {'zone': zone} metadata['zone_include'] = metadata['zone_include'] + txt for zone in self.__reverse_zones().keys(): # IPv6 zones are : delimited if ":" in zone: # if IPv6, assume xxxx:xxxx:xxxx:xxxx # 0123456789012345678 long_zone = (self.__expand_IPv6(zone + '::1'))[:19] tokens = list(re.sub(':', '', long_zone)) tokens.reverse() arpa = '.'.join(tokens) + '.ip6.arpa' else: # IPv4 address split by '.' tokens = zone.split('.') tokens.reverse() arpa = '.'.join(tokens) + '.in-addr.arpa' # metadata['reverse_zones'].append((zone, arpa)) txt = """ zone "%(arpa)s." { type master; file "%(zone)s"; }; """ % {'arpa': arpa, 'zone': zone} metadata['zone_include'] = metadata['zone_include'] + txt try: f2 = open(template_file, "r") except: raise CX(_("error reading template from file: %s") % template_file) template_data = "" template_data = f2.read() f2.close() if self.logger is not None: self.logger.info("generating %s" % settings_file) self.templar.render(template_data, metadata, settings_file, None)
def set_supported_boot_loaders(self, supported_boot_loaders): """ Some distributions, particularly on powerpc, can only be netbooted using specific bootloaders. """ if len(supported_boot_loaders) < 1: raise CX(_("No valid supported boot loaders specified for distro '%s'" % self.name)) self.supported_boot_loaders = supported_boot_loaders self.boot_loader = supported_boot_loaders[0]
def set_ipv6_address(self, address, interface): """ Assign a IP or hostname in DHCP when this MAC boots. Only works if manage_dhcp is set in /etc/cobbler/settings """ intf = self.__get_interface(interface) if address == "" or utils.is_ip(address): intf["ipv6_address"] = address.strip() return True raise CX(_("invalid format for IPv6 IP address (%s)") % address)
def set_kernel_options_post(self, options): """ Post kernel options are a space delimited list, like 'a=b c=d e=f g h i=j' or a dict. """ (success, value) = utils.input_string_or_dict(options, allow_multiples=True) if not success: raise CX(_("invalid post kernel options")) else: self.kernel_options_post = value
def set_yumopts(self, options): """ Kernel options are a space delimited list, like 'a=b c=d e=f g h i=j' or a dictionary. """ (success, value) = utils.input_string_or_dict(options, allow_multiples=False) if not success: raise CX(_("invalid yum options")) else: self.yumopts = value
def set_environment(self, options): """ Yum can take options from the environment. This puts them there before each reposync. """ (success, value) = utils.input_string_or_dict(options, allow_multiples=False) if not success: raise CX(_("invalid environment options")) else: self.environment = value
def set_priority(self, priority): """ Set the priority of the repository. 1= highest, 99=default Only works if host is using priorities plugin for yum. """ try: priority = int(str(priority)) except: raise CX(_("invalid priority level: %s") % priority) self.priority = priority
def set_ipv6_secondaries(self, addresses, interface): intf = self.__get_interface(interface) data = utils.input_string_or_list(addresses) secondaries = [] for address in data: if address == "" or utils.is_ip(address): secondaries.append(address) else: raise CX(_("invalid format for IPv6 IP address (%s)") % address) intf["ipv6_secondaries"] = secondaries
def set_image_type(self, image_type): """ Indicates what type of image this is. direct = something like "memdisk", physical only iso = a bootable ISO that pxe's or can be used for virt installs, virtual only virt-clone = a cloned virtual disk (FIXME: not yet supported), virtual only memdisk = hdd image (physical only) """ if image_type not in self.get_valid_image_types(): raise CX(_("image type must be on of the following: %s") % string.join(self.get_valid_image_types(), ", ")) self.image_type = image_type
def set_initrd(self, initrd): """ Specifies an initrd image. Path search works as in set_kernel. File must be named appropriately. """ if initrd is None or initrd == "": raise CX("initrd not specified") if utils.find_initrd(initrd): self.initrd = initrd return raise CX(_("initrd not found"))
def generate_autoinstall_for_profile(self, g): g = self.api.find_profile(name=g) if g is None: return "# profile not found" distro = g.get_conceptual_parent() if distro is None: raise CX(_("profile %(profile)s references missing distro %(distro)s") % {"profile": g.name, "distro": g.distro}) return self.generate_autoinstall(profile=g)
def set_boot_loader(self, name): try: # If we have already loaded the supported boot loaders from # the signature, use that data supported_distro_boot_loaders = self.supported_boot_loaders except: # otherwise, refresh from the signatures / defaults self.supported_boot_loaders = utils.get_supported_distro_boot_loaders(self) supported_distro_boot_loaders = self.supported_boot_loaders if name not in supported_distro_boot_loaders: raise CX(_("Invalid boot loader name: %s. Supported boot loaders are: %s" % (name, ' '.join(supported_distro_boot_loaders)))) self.boot_loader = name
def delete_interface(self, name): """ Used to remove an interface. """ if name in self.interfaces and len(self.interfaces) > 1: del self.interfaces[name] else: if name not in self.interfaces: # no interface here to delete pass else: raise CX(_("At least one interface needs to be defined."))
def set_mgmt_parameters(self, mgmt_parameters): """ A YAML string which can be assigned to any object, this is used by Puppet's external_nodes feature. """ if mgmt_parameters == "<<inherit>>": self.mgmt_parameters = mgmt_parameters else: import yaml data = yaml.safe_load(mgmt_parameters) if type(data) is not dict: raise CX(_("Input YAML in Puppet Parameter field must evaluate to a dictionary.")) self.mgmt_parameters = data
def remove(self, name, with_delete=True, with_sync=True, with_triggers=True, recursive=False, logger=None): """ Remove element named 'name' from the collection """ name = name.lower() if not recursive: for v in self.collection_mgr.systems(): if v.profile is not None and v.profile.lower() == name: raise CX(_("removal would orphan system: %s") % v.name) obj = self.find(name=name) if obj is not None: if recursive: kids = obj.get_children() for k in kids: if k.COLLECTION_TYPE == "profile": self.collection_mgr.api.remove_profile(k.name, recursive=recursive, delete=with_delete, with_triggers=with_triggers, logger=logger) else: self.collection_mgr.api.remove_system(k.name, recursive=recursive, delete=with_delete, with_triggers=with_triggers, logger=logger) if with_delete: if with_triggers: utils.run_triggers(self.collection_mgr.api, obj, "/var/lib/cobbler/triggers/delete/profile/pre/*", [], logger) self.lock.acquire() try: del self.listing[name] finally: self.lock.release() self.collection_mgr.serialize_delete(self, obj) if with_delete: if with_triggers: utils.run_triggers(self.collection_mgr.api, obj, "/var/lib/cobbler/triggers/delete/profile/post/*", [], logger) utils.run_triggers(self.collection_mgr.api, obj, "/var/lib/cobbler/triggers/change/*", [], logger) if with_sync: lite_sync = action_litesync.CobblerLiteSync(self.collection_mgr, logger=logger) lite_sync.remove_single_profile(name) return raise CX(_("cannot delete an object that does not exist: %s") % name)
def __parse_config(): etcfile='/etc/cobbler/users.conf' if not os.path.exists(etcfile): raise CX(_("/etc/cobbler/users.conf does not exist")) config = ConfigParser.ConfigParser() config.read(etcfile) alldata = {} sections = config.sections() for g in sections: alldata[str(g)] = {} opts = config.options(g) for o in opts: alldata[g][o] = 1 return alldata
def find(self, name=None, return_list=False, no_errors=False, **kargs): """ Return first object in the collection that maches all item='value' pairs passed, else return None if no objects can be found. When return_list is set, can also return a list. Empty list would be returned instead of None in that case. """ matches = [] # support the old style innovation without kwargs if name is not None: kargs["name"] = name kargs = self.__rekey(kargs) # no arguments is an error, so we don't return a false match if len(kargs) == 0: raise CX(_("calling find with no arguments")) # performance: if the only key is name we can skip the whole loop if len(kargs) == 1 and "name" in kargs and not return_list: return self.listing.get(kargs["name"].lower(), None) self.lock.acquire() try: for (name, obj) in self.listing.iteritems(): if obj.find_match(kargs, no_errors=no_errors): matches.append(obj) finally: self.lock.release() if not return_list: if len(matches) == 0: return None return matches[0] else: return matches
def __find_compare(self, from_search, from_obj): if isinstance(from_obj, basestring): # FIXME: fnmatch is only used for string to string comparisions # which should cover most major usage, if not, this deserves fixing if fnmatch.fnmatch(from_obj.lower(), from_search.lower()): return True else: return False else: if isinstance(from_search, basestring): if isinstance(from_obj, list): from_search = utils.input_string_or_list(from_search) for x in from_search: if x not in from_obj: return False return True if isinstance(from_obj, dict): (junk, from_search) = utils.input_string_or_dict( from_search, allow_multiples=True) for x in from_search.keys(): y = from_search[x] if x not in from_obj: return False if not (y == from_obj[x]): return False return True if isinstance(from_obj, bool): if from_search.lower() in ["true", "1", "y", "yes"]: inp = True else: inp = False if inp == from_obj: return True return False raise CX(_("find cannot compare type: %s") % type(from_obj))
def write_genders_file(config, profiles_genders, distros_genders, mgmtcls_genders): """ genders file is over-written when manage_genders is set in /var/lib/cobbler/settings. """ templar_inst = cobbler.templar.Templar(config) try: f2 = open(template_file, "r") except: raise CX(_("error reading template: %s") % template_file) template_data = "" template_data = f2.read() f2.close() metadata = { "date": time.asctime(time.gmtime()), "profiles_genders": profiles_genders, "distros_genders": distros_genders, "mgmtcls_genders": mgmtcls_genders } templar_inst.render(template_data, metadata, settings_file, None)
def set_ipv6_default_gateway(self, address, interface): intf = self.__get_interface(interface) if address == "" or utils.is_ip(address): intf["ipv6_default_gateway"] = address.strip() return True raise CX(_("invalid format for IPv6 IP address (%s)") % address)
def rename(self, ref, newname, with_sync=True, with_triggers=True, logger=None): """ Allows an object "ref" to be given a newname without affecting the rest of the object tree. """ # Nothing to do when it is the same name if newname == ref.name: return True # make a copy of the object, but give it a new name. oldname = ref.name newref = ref.make_clone() newref.set_name(newname) self.add(newref, with_triggers=with_triggers, save=True) # for mgmt classes, update all objects that use it if ref.COLLECTION_TYPE == "mgmtclass": for what in ["distro", "profile", "system"]: items = self.api.find_items(what, {"mgmt_classes": oldname}) for item in items: for i in range(0, len(item.mgmt_classes)): if item.mgmt_classes[i] == oldname: item.mgmt_classes[i] = newname self.api.add_item(what, item, save=True) # for a repo, rename the mirror directory if ref.COLLECTION_TYPE == "repo": path = "/var/www/cobbler/repo_mirror/%s" % ref.name if os.path.exists(path): newpath = "/var/www/cobbler/repo_mirror/%s" % newref.name os.renames(path, newpath) # for a distro, rename the mirror and references to it if ref.COLLECTION_TYPE == 'distro': path = utils.find_distro_path(self.api.settings(), ref) # create a symlink for the new distro name utils.link_distro(self.api.settings(), newref) # test to see if the distro path is based directly # on the name of the distro. If it is, things need # to updated accordingly if os.path.exists(path) and path == "/var/www/cobbler/ks_mirror/%s" % ref.name: newpath = "/var/www/cobbler/ks_mirror/%s" % newref.name os.renames(path, newpath) # update any reference to this path ... distros = self.api.distros() for d in distros: if d.kernel.find(path) == 0: d.set_kernel(d.kernel.replace(path, newpath)) d.set_initrd(d.initrd.replace(path, newpath)) self.config.serialize_item(self, d) # now descend to any direct ancestors and point them at the new object allowing # the original object to be removed without orphanage. Direct ancestors # will either be profiles or systems. Note that we do have to care as # set_parent is only really meaningful for subprofiles. We ideally want a more # generic set_parent. kids = ref.get_children() for k in kids: if k.COLLECTION_TYPE == "distro": raise CX(_("internal error, not expected to have distro child objects")) elif k.COLLECTION_TYPE == "profile": if k.parent != "": k.set_parent(newname) else: k.set_distro(newname) self.api.profiles().add(k, save=True, with_sync=with_sync, with_triggers=with_triggers) elif k.COLLECTION_TYPE == "system": k.set_profile(newname) self.api.systems().add(k, save=True, with_sync=with_sync, with_triggers=with_triggers) elif k.COLLECTION_TYPE == "repo": raise CX(_("internal error, not expected to have repo child objects")) else: raise CX(_("internal error, unknown child type (%s), cannot finish rename" % k.COLLECTION_TYPE)) # now delete the old version self.remove(oldname, with_delete=True, with_triggers=with_triggers) return True
def write_dhcp_file(self): """ DHCP files are written when manage_dhcp is set in /etc/cobbler/settings. """ template_file = "/etc/cobbler/dhcp.template" blender_cache = {} try: f2 = open(template_file, "r") except: raise CX(_("error reading template: %s") % template_file) template_data = "" template_data = f2.read() f2.close() # use a simple counter for generating generic names where a hostname # is not available counter = 0 # we used to just loop through each system, but now we must loop # through each network interface of each system. dhcp_tags = {"default": {}} yaboot = "/yaboot" # FIXME: ding should evolve into the new dhcp_tags dict ding = {} for system in self.systems: if not system.is_management_supported(cidr_ok=False): continue profile = system.get_conceptual_parent() distro = profile.get_conceptual_parent() # if distro is None then the profile is really an image record for (name, interface) in system.interfaces.iteritems(): # this is really not a per-interface setting # but we do this to make the templates work # without upgrade interface["gateway"] = system.gateway mac = interface["mac_address"] if interface["interface_type"] in ("bond_slave", "bridge_slave", "bonded_bridge_slave"): if interface["interface_master"] not in system.interfaces: # Can't write DHCP entry; master interface does not exist continue if system.name not in ding: ding[system.name] = {interface["interface_master"]: []} else: ding[system.name][interface["interface_master"]] = [] if len(ding[system.name][ interface["interface_master"]]) == 0: ding[system.name][ interface["interface_master"]].append(mac) ip = system.interfaces[ interface["interface_master"]]["ip_address"] netmask = system.interfaces[ interface["interface_master"]]["netmask"] dhcp_tag = system.interfaces[ interface["interface_master"]]["dhcp_tag"] host = system.interfaces[ interface["interface_master"]]["dns_name"] if ip is None or ip == "": for (nam2, int2) in system.interfaces.iteritems(): if (nam2.startswith(interface["interface_master"] + ".") and int2["ip_address"] is not None and int2["ip_address"] != ""): ip = int2["ip_address"] break interface["ip_address"] = ip interface["netmask"] = netmask else: ip = interface["ip_address"] netmask = interface["netmask"] dhcp_tag = interface["dhcp_tag"] host = interface["dns_name"] if distro is not None: interface["distro"] = distro.to_dict() if mac is None or mac == "": # can't write a DHCP entry for this system continue counter = counter + 1 # the label the entry after the hostname if possible if host is not None and host != "": if name != "eth0": interface["name"] = "%s-%s" % (host, name) else: interface["name"] = "%s" % (host) else: interface["name"] = "generic%d" % counter # add references to the system, profile, and distro # for use in the template if system.name in blender_cache: blended_system = blender_cache[system.name] else: blended_system = utils.blender(self.api, False, system) blender_cache[system.name] = blended_system interface["next_server"] = blended_system["next_server"] interface["netboot_enabled"] = blended_system[ "netboot_enabled"] interface["hostname"] = blended_system["hostname"] interface["owner"] = blended_system["name"] interface["enable_gpxe"] = blended_system["enable_gpxe"] interface["name_servers"] = blended_system["name_servers"] if not self.settings.always_write_dhcp_entries: if not interface["netboot_enabled"] and interface['static']: continue if distro is not None: if distro.arch.startswith("ppc"): if blended_system["boot_loader"] == "pxelinux": del interface["filename"] elif distro.boot_loader == "grub2" or blended_system[ "boot_loader"] == "grub2": interface[ "filename"] = "boot/grub/powerpc-ieee1275/core.elf" else: interface["filename"] = yaboot if dhcp_tag == "": dhcp_tag = blended_system["dhcp_tag"] if dhcp_tag == "": dhcp_tag = "default" if dhcp_tag not in dhcp_tags: dhcp_tags[dhcp_tag] = {mac: interface} else: dhcp_tags[dhcp_tag][mac] = interface # we are now done with the looping through each interface of each system metadata = { "date": time.asctime(time.gmtime()), "cobbler_server": "%s:%s" % (self.settings.server, self.settings.http_port), "next_server": self.settings.next_server, "yaboot": yaboot, "dhcp_tags": dhcp_tags } if self.logger is not None: self.logger.info("generating %s" % self.settings_file) self.templar.render(template_data, metadata, self.settings_file, None)
class Distro(item.Item): """ A Cobbler distribution object """ TYPE_NAME = _("distro") COLLECTION_TYPE = "distro" def __init__(self, *args, **kwargs): """ This creates a Distro object. :param args: Place for extra parameters in this distro object. :param kwargs: Place for extra parameters in this distro object. """ super(Distro, self).__init__(*args, **kwargs) self.kernel_options = {} self.kernel_options_post = {} self.autoinstall_meta = {} self.source_repos = [] self.fetchable_files = {} self.boot_files = {} self.template_files = {} self.remote_grub_kernel = "" self.remote_grub_initrd = "" # # override some base class methods first (item.Item) # def make_clone(self): """ Clone a distro object. :return: The cloned object. Not persisted on the disk or in a database. """ _dict = self.to_dict() cloned = Distro(self.collection_mgr) cloned.from_dict(_dict) return cloned def get_fields(self): """ Return the list of fields and their properties """ return FIELDS def get_parent(self): """ Distros don't have parent objects. """ return None def check_if_valid(self): """ Check if a distro object is valid. If invalid an exception is raised. """ if self.name is None: raise CX("name is required") if self.kernel is None: raise CX("Error with distro %s - kernel is required" % (self.name)) if self.initrd is None: raise CX("Error with distro %s - initrd is required" % (self.name)) # self.remote_grub_kernel has to be set in set_remote_boot_kernel and here # in case the distro is read from json file (setters are not called). if self.remote_boot_kernel: self.remote_grub_kernel = grub.parse_grub_remote_file( self.remote_boot_kernel) if not self.remote_grub_kernel: raise CX("Invalid URL for remote boot kernel: %s" % self.remote_boot_kernel) if self.remote_boot_initrd: self.remote_grub_initrd = grub.parse_grub_remote_file( self.remote_boot_initrd) if not self.remote_grub_initrd: raise CX("Invalid URL for remote boot initrd: %s" % self.remote_boot_initrd) if utils.file_is_remote(self.kernel): if not utils.remote_file_exists(self.kernel): raise CX("Error with distro %s - kernel '%s' not found" % (self.name, self.kernel)) elif not os.path.exists(self.kernel): raise CX("Error with distro %s - kernel '%s' not found" % (self.name, self.kernel)) if utils.file_is_remote(self.initrd): if not utils.remote_file_exists(self.initrd): raise CX("Error with distro %s - initrd path '%s' not found" % (self.name, self.initrd)) elif not os.path.exists(self.initrd): raise CX("Error with distro %s - initrd path '%s' not found" % (self.name, self.initrd)) # # specific methods for item.Distro # def set_kernel(self, kernel): """ Specifies a kernel. The kernel parameter is a full path, a filename in the configured kernel directory (set in /etc/cobbler.conf) or a directory path that would contain a selectable kernel. Kernel naming conventions are checked, see docs in the utils module for ``find_kernel``. :param kernel: :raises CX: If the kernel was not found """ if kernel is None or kernel == "": raise CX("kernel not specified") if utils.find_kernel(kernel): self.kernel = kernel return raise CX("kernel not found: %s" % kernel) def set_remote_boot_kernel(self, remote_boot_kernel): """ URL to a remote kernel. If the bootloader supports this feature, it directly tries to retrieve the kernel and boot it. (grub supports tftp and http protocol and server must be an IP). """ if remote_boot_kernel: self.remote_grub_kernel = grub.parse_grub_remote_file( remote_boot_kernel) if not self.remote_grub_kernel: raise CX("Invalid URL for remote boot kernel: %s" % remote_boot_kernel) self.remote_boot_kernel = remote_boot_kernel return # Set to None or "" self.remote_grub_kernel = self.remote_boot_kernel = remote_boot_kernel def set_tree_build_time(self, datestamp): """ Sets the import time of the distro. If not imported, this field is not meaningful. :param datestamp: The datestamp to save the builddate. There is an attempt to convert it to a float, so please make sure it is compatible to this. """ self.tree_build_time = float(datestamp) def set_breed(self, breed): """ Set the Operating system breed. :param breed: The new breed to set. """ return utils.set_breed(self, breed) def set_os_version(self, os_version): """ Set the Operating System Version. :param os_version: The new OS Version. """ return utils.set_os_version(self, os_version) def set_initrd(self, initrd): """ Specifies an initrd image. Path search works as in set_kernel. File must be named appropriately. :param initrd: The new path to the ``initrd``. """ if initrd is None or initrd == "": raise CX("initrd not specified") if utils.find_initrd(initrd): self.initrd = initrd return raise CX(_("initrd not found")) def set_remote_boot_initrd(self, remote_boot_initrd): """ URL to a remote initrd. If the bootloader supports this feature, it directly tries to retrieve the initrd and boot it. (grub supports tftp and http protocol and server must be an IP). """ if remote_boot_initrd: self.remote_grub_initrd = grub.parse_grub_remote_file( remote_boot_initrd) if not self.remote_grub_initrd: raise CX("Invalid URL for remote boot initrd: %s" % remote_boot_initrd) self.remote_boot_initrd = remote_boot_initrd return # Set to None or "" self.remote_grub_initrd = self.remote_boot_initrd = remote_boot_initrd def set_source_repos(self, repos): """ A list of http:// URLs on the Cobbler server that point to yum configuration files that can be used to install core packages. Use by ``cobbler import`` only. :param repos: The list of URLs. """ self.source_repos = repos def set_arch(self, arch): """ The field is mainly relevant to PXE provisioning. Using an alternative distro type allows for dhcpd.conf templating to "do the right thing" with those systems -- this also relates to bootloader configuration files which have different syntax for different distro types (because of the bootloaders). This field is named "arch" because mainly on Linux, we only care about the architecture, though if (in the future) new provisioning types are added, an arch value might be something like "bsd_x86". :param arch: The architecture of the operating system distro. """ return utils.set_arch(self, arch) def get_arch(self): """ Return the architecture of the distribution :return: Return the current architecture. """ return self.arch def set_supported_boot_loaders(self, supported_boot_loaders): """ Some distributions, particularly on powerpc, can only be netbooted using specific bootloaders. :param supported_boot_loaders: The bootloaders which are available for being set. """ if len(supported_boot_loaders) < 1: raise CX( _("No valid supported boot loaders specified for distro '%s'" % self.name)) self.supported_boot_loaders = supported_boot_loaders self.boot_loader = supported_boot_loaders[0] def set_boot_loader(self, name): """ Set the bootloader for the distro. :param name: The name of the bootloader. Must be one of the supported ones. """ try: # If we have already loaded the supported boot loaders from # the signature, use that data supported_distro_boot_loaders = self.supported_boot_loaders except: # otherwise, refresh from the signatures / defaults self.supported_boot_loaders = utils.get_supported_distro_boot_loaders( self) supported_distro_boot_loaders = self.supported_boot_loaders if name not in supported_distro_boot_loaders: raise CX( _("Invalid boot loader name: %s. Supported boot loaders are: %s" % (name, ' '.join(supported_distro_boot_loaders)))) self.boot_loader = name def set_redhat_management_key(self, management_key): """ Set the redhat management key. This is probably only needed if you have spacewalk, uyuni or SUSE Manager running. :param management_key: The redhat management key. """ if management_key is None: self.redhat_management_key = "" self.redhat_management_key = management_key def get_redhat_management_key(self): """ Get the redhat management key. This is probably only needed if you have spacewalk, uyuni or SUSE Manager running. :return: The key as a string. :rtype: str """ return self.redhat_management_key
def write_dhcp_file(self): """ DHCP files are written when manage_dhcp is set in /var/lib/cobbler/settings. """ settings_file = "/etc/dnsmasq.conf" template_file = "/etc/cobbler/dnsmasq.template" try: f2 = open(template_file, "r") except: raise CX(_("error writing template to file: %s") % template_file) template_data = "" template_data = f2.read() f2.close() system_definitions = {} counter = 0 # we used to just loop through each system, but now we must loop # through each network interface of each system. for system in self.systems: if not system.is_management_supported(cidr_ok=False): continue profile = system.get_conceptual_parent() distro = profile.get_conceptual_parent() for (name, interface) in list(system.interfaces.items()): mac = interface["mac_address"] ip = interface["ip_address"] host = interface["dns_name"] ipv6 = interface["ipv6_address"] if mac is None or mac == "": # can't write a DHCP entry for this system continue counter += 1 # In many reallife situations there is a need to control the IP address # and hostname for a specific client when only the MAC address is available. # In addition to that in some scenarios there is a need to explicitly # label a host with the applicable architecture in order to correctly # handle situations where we need something other than pxelinux.0. # So we always write a dhcp-host entry with as much info as possible # to allow maximum control and flexibility within the dnsmasq config systxt = "dhcp-host=net:" + distro.arch.lower() + "," + mac if host is not None and host != "": systxt += "," + host if ip is not None and ip != "": systxt += "," + ip if ipv6 is not None and ipv6 != "": systxt += ",[%s]" % ipv6 systxt += "\n" dhcp_tag = interface["dhcp_tag"] if dhcp_tag == "": dhcp_tag = "default" if dhcp_tag not in system_definitions: system_definitions[dhcp_tag] = "" system_definitions[ dhcp_tag] = system_definitions[dhcp_tag] + systxt # we are now done with the looping through each interface of each system metadata = { "insert_cobbler_system_definitions": system_definitions.get("default", ""), "date": time.asctime(time.gmtime()), "cobbler_server": self.settings.server, "next_server": self.settings.next_server, } # now add in other DHCP expansions that are not tagged with "default" for x in list(system_definitions.keys()): if x == "default": continue metadata["insert_cobbler_system_definitions_%s" % x] = system_definitions[x] self.templar.render(template_data, metadata, settings_file, None)
def remove(self, name, with_delete=True, with_sync=True, with_triggers=True, recursive=False, logger=None): """ Remove element named 'name' from the collection """ name = name.lower() if not recursive: for v in self.collection_mgr.systems(): if v.profile is not None and v.profile.lower() == name: raise CX(_("removal would orphan system: %s") % v.name) obj = self.find(name=name) if obj is not None: if recursive: kids = obj.get_children() for k in kids: if k.COLLECTION_TYPE == "profile": self.collection_mgr.api.remove_profile( k.name, recursive=recursive, delete=with_delete, with_triggers=with_triggers, logger=logger) else: self.collection_mgr.api.remove_system( k.name, recursive=recursive, delete=with_delete, with_triggers=with_triggers, logger=logger) if with_delete: if with_triggers: utils.run_triggers( self.collection_mgr.api, obj, "/var/lib/cobbler/triggers/delete/profile/pre/*", [], logger) self.lock.acquire() try: del self.listing[name] finally: self.lock.release() self.collection_mgr.serialize_delete(self, obj) if with_delete: if with_triggers: utils.run_triggers( self.collection_mgr.api, obj, "/var/lib/cobbler/triggers/delete/profile/post/*", [], logger) utils.run_triggers(self.collection_mgr.api, obj, "/var/lib/cobbler/triggers/change/*", [], logger) if with_sync: lite_sync = action_litesync.CobblerLiteSync( self.collection_mgr, logger=logger) lite_sync.remove_single_profile(name) return raise CX(_("cannot delete an object that does not exist: %s") % name)
def remove(self, name, with_delete=True, with_sync=True, with_triggers=True, recursive=False, logger=None): """ Remove element named 'name' from the collection """ name = name.lower() # first see if any Groups use this distro if not recursive: for v in self.collection_mgr.profiles(): if v.distro and v.distro.lower() == name: raise CX(_("removal would orphan profile: %s") % v.name) obj = self.find(name=name) if obj is not None: kernel = obj.kernel if recursive: kids = obj.get_children() for k in kids: self.collection_mgr.api.remove_profile( k.name, recursive=recursive, delete=with_delete, with_triggers=with_triggers, logger=logger) if with_delete: if with_triggers: utils.run_triggers( self.collection_mgr.api, obj, "/var/lib/cobbler/triggers/delete/distro/pre/*", [], logger) if with_sync: lite_sync = litesync.CobblerLiteSync(self.collection_mgr, logger=logger) lite_sync.remove_single_distro(name) self.lock.acquire() try: del self.listing[name] finally: self.lock.release() self.collection_mgr.serialize_delete(self, obj) if with_delete: if with_triggers: utils.run_triggers( self.collection_mgr.api, obj, "/var/lib/cobbler/triggers/delete/distro/post/*", [], logger) utils.run_triggers(self.collection_mgr.api, obj, "/var/lib/cobbler/triggers/change/*", [], logger) # look through all mirrored directories and find if any directory is holding # this particular distribution's kernel and initrd settings = self.collection_mgr.settings() possible_storage = glob.glob(settings.webdir + "/distro_mirror/*") path = None for storage in possible_storage: if os.path.dirname(obj.kernel).find(storage) != -1: path = storage continue # if we found a mirrored path above, we can delete the mirrored storage /if/ # no other object is using the same mirrored storage. if with_delete and path is not None and os.path.exists( path) and kernel.find(settings.webdir) != -1: # this distro was originally imported so we know we can clean up the associated # storage as long as nothing else is also using this storage. found = False distros = self.api.distros() for d in distros: if d.kernel.find(path) != -1: found = True if not found: utils.rmtree(path)
def __duplication_checks(self, ref, check_for_duplicate_names, check_for_duplicate_netinfo): """ Prevents adding objects with the same name. Prevents adding or editing to provide the same IP, or MAC. Enforcement is based on whether the API caller requests it. """ # always protect against duplicate names if check_for_duplicate_names: match = None if isinstance(ref, item_system.System): match = self.api.find_system(ref.name) elif isinstance(ref, item_profile.Profile): match = self.api.find_profile(ref.name) elif isinstance(ref, item_distro.Distro): match = self.api.find_distro(ref.name) elif isinstance(ref, item_repo.Repo): match = self.api.find_repo(ref.name) elif isinstance(ref, item_image.Image): match = self.api.find_image(ref.name) elif isinstance(ref, item_mgmtclass.Mgmtclass): match = self.api.find_mgmtclass(ref.name) elif isinstance(ref, item_package.Package): match = self.api.find_package(ref.name) elif isinstance(ref, item_file.File): match = self.api.find_file(ref.name) else: raise CX("internal error, unknown object type") if match: raise CX(_("An object already exists with that name. Try 'edit'?")) # the duplicate mac/ip checks can be disabled. if not check_for_duplicate_netinfo: return if isinstance(ref, item_system.System): for (name, intf) in ref.interfaces.iteritems(): match_ip = [] match_mac = [] match_hosts = [] input_mac = intf["mac_address"] input_ip = intf["ip_address"] input_dns = intf["dns_name"] if not self.api.settings().allow_duplicate_macs and input_mac is not None and input_mac != "": match_mac = self.api.find_system(mac_address=input_mac, return_list=True) if not self.api.settings().allow_duplicate_ips and input_ip is not None and input_ip != "": match_ip = self.api.find_system(ip_address=input_ip, return_list=True) # it's ok to conflict with your own net info. if not self.api.settings().allow_duplicate_hostnames and input_dns is not None and input_dns != "": match_hosts = self.api.find_system(dns_name=input_dns, return_list=True) for x in match_mac: if x.name != ref.name: raise CX(_("Can't save system %s. The MAC address (%s) is already used by system %s (%s)") % (ref.name, intf["mac_address"], x.name, name)) for x in match_ip: if x.name != ref.name: raise CX(_("Can't save system %s. The IP address (%s) is already used by system %s (%s)") % (ref.name, intf["ip_address"], x.name, name)) for x in match_hosts: if x.name != ref.name: raise CX(_("Can't save system %s. The dns name (%s) is already used by system %s (%s)") % (ref.name, intf["dns_name"], x.name, name))
class System(item.Item): """ A Cobbler system object. """ TYPE_NAME = _("system") COLLECTION_TYPE = "system" def __init__(self, *args, **kwargs): super(System, self).__init__(*args, **kwargs) self.interfaces = dict() self.kernel_options = {} self.kernel_options_post = {} self.autoinstall_meta = {} self.fetchable_files = {} self.boot_files = {} self.template_files = {} # # override some base class methods first (item.Item) # def get_fields(self): return FIELDS def make_clone(self): _dict = self.to_dict() cloned = System(self.collection_mgr) cloned.from_dict(_dict) return cloned def from_dict(self, seed_data): # FIXME: most definitely doesn't grok interfaces yet. return utils.from_dict_from_fields(self, seed_data, FIELDS) def get_parent(self): """ Return object next highest up the tree. """ if (self.parent is None or self.parent == '') and self.profile: return self.collection_mgr.profiles().find(name=self.profile) elif (self.parent is None or self.parent == '') and self.image: return self.collection_mgr.images().find(name=self.image) else: return self.collection_mgr.systems().find(name=self.parent) def check_if_valid(self): if self.name is None or self.name == "": raise CX("name is required") if self.profile is None or self.profile == "": if self.image is None or self.image == "": raise CX( "Error with system %s - profile or image is required" % (self.name)) # # specific methods for item.System # def __create_interface(self, interface): self.interfaces[interface] = {} for field in NETWORK_INTERFACE_FIELDS: self.interfaces[interface][field[0]] = field[1] def __get_interface(self, name): if not name: raise CX(_("No network interface name provided")) if name not in self.interfaces: self.__create_interface(name) return self.interfaces[name] def delete_interface(self, name): """ Used to remove an interface. """ if name in self.interfaces and len(self.interfaces) > 1: del self.interfaces[name] else: if name not in self.interfaces: # no interface here to delete pass else: raise CX(_("At least one interface needs to be defined.")) def rename_interface(self, names): """ Used to rename an interface. """ (name, newname) = names if name not in self.interfaces: raise CX(_("Interface %s does not exist" % name)) if newname in self.interfaces: raise CX(_("Interface %s already exists" % newname)) else: self.interfaces[newname] = self.interfaces[name] del self.interfaces[name] def set_boot_loader(self, name): if name not in utils.get_supported_system_boot_loaders(): raise CX(_("Invalid boot loader name: %s" % name)) self.boot_loader = name def set_server(self, server): """ If a system can't reach the boot server at the value configured in settings because it doesn't have the same name on it's subnet this is there for an override. """ if server is None or server == "": server = "<<inherit>>" self.server = server def set_next_server(self, server): if server is None or server == "" or server == "<<inherit>>": self.next_server = "<<inherit>>" else: server = server.strip() self.next_server = validate.ipv4_address(server) def set_proxy(self, proxy): if proxy is None or proxy == "": proxy = "<<inherit>>" self.proxy = proxy def get_mac_address(self, interface): """ Get the mac address, which may be implicit in the object name or explicit with --mac-address. Use the explicit location first. """ intf = self.__get_interface(interface) if intf["mac_address"] != "": return intf["mac_address"].strip() else: return None def get_ip_address(self, interface): """ Get the IP address for the given interface. """ intf = self.__get_interface(interface) if intf["ip_address"] != "": return intf["ip_address"].strip() else: return "" def is_management_supported(self, cidr_ok=True): """ Can only add system PXE records if a MAC or IP address is available, else it's a koan only record. """ if self.name == "default": return True for (name, x) in list(self.interfaces.items()): mac = x.get("mac_address", None) ip = x.get("ip_address", None) if ip is not None and not cidr_ok and ip.find("/") != -1: # ip is in CIDR notation return False if mac is not None or ip is not None: # has ip and/or mac return True return False def set_dhcp_tag(self, dhcp_tag, interface): intf = self.__get_interface(interface) intf["dhcp_tag"] = dhcp_tag def set_cnames(self, cnames, interface): intf = self.__get_interface(interface) data = utils.input_string_or_list(cnames) intf["cnames"] = data def set_static_routes(self, routes, interface): intf = self.__get_interface(interface) data = utils.input_string_or_list(routes) intf["static_routes"] = data def set_status(self, status): self.status = status def set_static(self, truthiness, interface): intf = self.__get_interface(interface) intf["static"] = utils.input_boolean(truthiness) def set_management(self, truthiness, interface): intf = self.__get_interface(interface) intf["management"] = utils.input_boolean(truthiness) # --- def set_dns_name(self, dns_name, interface): """ Set DNS name for interface. @param: str dns_name (dns name) @param: str interface (interface name) @returns: True or CX """ dns_name = validate.hostname(dns_name) if dns_name != "" and utils.input_boolean( self.collection_mgr._settings.allow_duplicate_hostnames ) is False: matched = self.collection_mgr.api.find_items( "system", {"dns_name": dns_name}) for x in matched: if x.name != self.name: raise CX("DNS name duplicated: %s" % dns_name) intf = self.__get_interface(interface) intf["dns_name"] = dns_name def set_hostname(self, hostname): """ Set hostname. @param: str hostname (hostname for system) @returns: True or CX """ self.hostname = validate.hostname(hostname) def set_ip_address(self, address, interface): """ Set IPv4 address on interface. @param: str address (ip address) @param: str interface (interface name) @returns: True or CX """ address = validate.ipv4_address(address) if address != "" and utils.input_boolean( self.collection_mgr._settings.allow_duplicate_ips) is False: matched = self.collection_mgr.api.find_items( "system", {"ip_address": address}) for x in matched: if x.name != self.name: raise CX("IP address duplicated: %s" % address) intf = self.__get_interface(interface) intf["ip_address"] = address def set_mac_address(self, address, interface): """ Set mac address on interface. @param: str address (mac address) @param: str interface (interface name) @returns: True or CX """ address = validate.mac_address(address) if address == "random": address = utils.get_random_mac(self.collection_mgr.api) if address != "" and utils.input_boolean( self.collection_mgr._settings.allow_duplicate_macs) is False: matched = self.collection_mgr.api.find_items( "system", {"mac_address": address}) for x in matched: if x.name != self.name: raise CX("MAC address duplicated: %s" % address) intf = self.__get_interface(interface) intf["mac_address"] = address def set_gateway(self, gateway): """ Set a gateway IPv4 address. @param: str gateway (ip address) @returns: True or CX """ self.gateway = validate.ipv4_address(gateway) def set_name_servers(self, data): """ Set the DNS servers. @param: str/list data (string or list of nameservers) @returns: True or CX """ self.name_servers = validate.name_servers(data) def set_name_servers_search(self, data): """ Set the DNS search paths. @param: str/list data (string or list of search domains) @returns: True or CX """ self.name_servers_search = validate.name_servers_search(data) def set_netmask(self, netmask, interface): """ Set the netmask for given interface. @param: str netmask (netmask) @param: str interface (interface name) @returns: True or CX """ intf = self.__get_interface(interface) intf["netmask"] = validate.ipv4_netmask(netmask) def set_if_gateway(self, gateway, interface): """ Set the per-interface gateway. @param: str gateway (ipv4 address for the gateway) @param: str interface (interface name) @returns: True or CX """ intf = self.__get_interface(interface) intf["if_gateway"] = validate.ipv4_address(gateway) # -- def set_virt_bridge(self, bridge, interface): if bridge == "": bridge = self.settings.default_virt_bridge intf = self.__get_interface(interface) intf["virt_bridge"] = bridge def set_interface_type(self, type, interface): interface_types = [ "bridge", "bridge_slave", "bond", "bond_slave", "bonded_bridge_slave", "bmc", "na", "infiniband", "" ] if type not in interface_types: raise CX( _("interface type value must be one of: %s or blank" % ",".join(interface_types))) if type == "na": type = "" intf = self.__get_interface(interface) intf["interface_type"] = type def set_interface_master(self, interface_master, interface): intf = self.__get_interface(interface) intf["interface_master"] = interface_master def set_bonding_opts(self, bonding_opts, interface): intf = self.__get_interface(interface) intf["bonding_opts"] = bonding_opts def set_bridge_opts(self, bridge_opts, interface): intf = self.__get_interface(interface) intf["bridge_opts"] = bridge_opts def set_ipv6_autoconfiguration(self, truthiness): self.ipv6_autoconfiguration = utils.input_boolean(truthiness) def set_ipv6_default_device(self, interface_name): if interface_name is None: interface_name = "" self.ipv6_default_device = interface_name def set_ipv6_address(self, address, interface): """ Set IPv6 address on interface. @param: str address (ip address) @param: str interface (interface name) @returns: True or CX """ address = validate.ipv6_address(address) if address != "" and utils.input_boolean( self.collection_mgr._settings.allow_duplicate_ips) is False: matched = self.collection_mgr.api.find_items( "system", {"ipv6_address": address}) for x in matched: if x.name != self.name: raise CX("IP address duplicated: %s" % address) intf = self.__get_interface(interface) intf["ipv6_address"] = address def set_ipv6_prefix(self, prefix, interface): """ Assign a IPv6 prefix """ intf = self.__get_interface(interface) intf["ipv6_prefix"] = prefix.strip() def set_ipv6_secondaries(self, addresses, interface): intf = self.__get_interface(interface) data = utils.input_string_or_list(addresses) secondaries = [] for address in data: if address == "" or utils.is_ip(address): secondaries.append(address) else: raise CX( _("invalid format for IPv6 IP address (%s)") % address) intf["ipv6_secondaries"] = secondaries def set_ipv6_default_gateway(self, address, interface): intf = self.__get_interface(interface) if address == "" or utils.is_ip(address): intf["ipv6_default_gateway"] = address.strip() return raise CX(_("invalid format for IPv6 IP address (%s)") % address) def set_ipv6_static_routes(self, routes, interface): intf = self.__get_interface(interface) data = utils.input_string_or_list(routes) intf["ipv6_static_routes"] = data def set_ipv6_mtu(self, mtu, interface): intf = self.__get_interface(interface) intf["ipv6_mtu"] = mtu def set_mtu(self, mtu, interface): intf = self.__get_interface(interface) intf["mtu"] = mtu def set_connected_mode(self, truthiness, interface): intf = self.__get_interface(interface) intf["connected_mode"] = utils.input_boolean(truthiness) def set_enable_gpxe(self, enable_gpxe): """ Sets whether or not the system will use gPXE for booting. """ self.enable_gpxe = utils.input_boolean(enable_gpxe) def set_profile(self, profile_name): """ Set the system to use a certain named profile. The profile must have already been loaded into the Profiles collection. """ old_parent = self.get_parent() if profile_name in ["delete", "None", "~", ""] or profile_name is None: self.profile = "" if isinstance(old_parent, item.Item): old_parent.children.pop(self.name, 'pass') return self.image = "" # mutual exclusion rule p = self.collection_mgr.profiles().find(name=profile_name) if p is not None: self.profile = profile_name self.depth = p.depth + 1 # subprofiles have varying depths. if isinstance(old_parent, item.Item): old_parent.children.pop(self.name, 'pass') new_parent = self.get_parent() if isinstance(new_parent, item.Item): new_parent.children[self.name] = self return raise CX(_("invalid profile name: %s") % profile_name) def set_image(self, image_name): """ Set the system to use a certain named image. Works like set_profile but cannot be used at the same time. It's one or the other. """ old_parent = self.get_parent() if image_name in ["delete", "None", "~", ""] or image_name is None: self.image = "" if isinstance(old_parent, item.Item): old_parent.children.pop(self.name, 'pass') return self.profile = "" # mutual exclusion rule img = self.collection_mgr.images().find(name=image_name) if img is not None: self.image = image_name self.depth = img.depth + 1 if isinstance(old_parent, item.Item): old_parent.children.pop(self.name, 'pass') new_parent = self.get_parent() if isinstance(new_parent, item.Item): new_parent.children[self.name] = self return raise CX(_("invalid image name (%s)") % image_name) def set_virt_cpus(self, num): return utils.set_virt_cpus(self, num) def set_virt_file_size(self, num): return utils.set_virt_file_size(self, num) def set_virt_disk_driver(self, driver): return utils.set_virt_disk_driver(self, driver) def set_virt_auto_boot(self, num): return utils.set_virt_auto_boot(self, num) def set_virt_pxe_boot(self, num): return utils.set_virt_pxe_boot(self, num) def set_virt_ram(self, num): return utils.set_virt_ram(self, num) def set_virt_type(self, vtype): return utils.set_virt_type(self, vtype) def set_virt_path(self, path): return utils.set_virt_path(self, path, for_system=True) def set_netboot_enabled(self, netboot_enabled): """ If true, allows per-system PXE files to be generated on sync (or add). If false, these files are not generated, thus eliminating the potential for an infinite install loop when systems are set to PXE boot first in the boot order. In general, users who are PXE booting first in the boot order won't create system definitions, so this feature primarily comes into play for programmatic users of the API, who want to initially create a system with netboot enabled and then disable it after the system installs, as triggered by some action in automatic installation file's %post section. For this reason, this option is not urfaced in the CLI, output, or documentation (yet). Use of this option does not affect the ability to use PXE menus. If an admin has machines set up to PXE only after local boot fails, this option isn't even relevant. """ self.netboot_enabled = utils.input_boolean(netboot_enabled) def set_autoinstall(self, autoinstall): """ Set the automatic installation template filepath, this must be a local file. @param str local automatic installation template file path """ autoinstall_mgr = autoinstall_manager.AutoInstallationManager( self.collection_mgr) self.autoinstall = autoinstall_mgr.validate_autoinstall_template_file_path( autoinstall) def set_power_type(self, power_type): if power_type is None: power_type = "" power_manager.validate_power_type(power_type) self.power_type = power_type def set_power_user(self, power_user): if power_user is None: power_user = "" utils.safe_filter(power_user) self.power_user = power_user def set_power_pass(self, power_pass): if power_pass is None: power_pass = "" utils.safe_filter(power_pass) self.power_pass = power_pass def set_power_address(self, power_address): if power_address is None: power_address = "" utils.safe_filter(power_address) self.power_address = power_address def set_power_id(self, power_id): if power_id is None: power_id = "" utils.safe_filter(power_id) self.power_id = power_id def modify_interface(self, _dict): """ Used by the WUI to modify an interface more-efficiently """ for (key, value) in list(_dict.items()): (field, interface) = key.split("-", 1) field = field.replace("_", "").replace("-", "") if field == "bondingopts": self.set_bonding_opts(value, interface) if field == "bridgeopts": self.set_bridge_opts(value, interface) if field == "connected_mode": self.set_connected_mode(value, interface) if field == "cnames": self.set_cnames(value, interface) if field == "dhcptag": self.set_dhcp_tag(value, interface) if field == "dnsname": self.set_dns_name(value, interface) if field == "ifgateway": self.set_if_gateway(value, interface) if field == "interfacetype": self.set_interface_type(value, interface) if field == "interfacemaster": self.set_interface_master(value, interface) if field == "ipaddress": self.set_ip_address(value, interface) if field == "ipv6address": self.set_ipv6_address(value, interface) if field == "ipv6defaultgateway": self.set_ipv6_default_gateway(value, interface) if field == "ipv6mtu": self.set_ipv6_mtu(value, interface) if field == "ipv6prefix": self.set_ipv6_prefix(value, interface) if field == "ipv6secondaries": self.set_ipv6_secondaries(value, interface) if field == "ipv6staticroutes": self.set_ipv6_static_routes(value, interface) if field == "macaddress": self.set_mac_address(value, interface) if field == "management": self.set_management(value, interface) if field == "mtu": self.set_mtu(value, interface) if field == "netmask": self.set_netmask(value, interface) if field == "static": self.set_static(value, interface) if field == "staticroutes": self.set_static_routes(value, interface) if field == "virtbridge": self.set_virt_bridge(value, interface) def set_repos_enabled(self, repos_enabled): self.repos_enabled = utils.input_boolean(repos_enabled) def set_serial_device(self, device_number): return utils.set_serial_device(self, device_number) def set_serial_baud_rate(self, baud_rate): return utils.set_serial_baud_rate(self, baud_rate) def get_config_filename(self, interface, loader=None): """ The configuration file for each system pxe uses is either a form of the MAC address of the hex version of the IP. If none of that is available, just use the given name, though the name given will be unsuitable for PXE configuration (For this, check system.is_management_supported()). This same file is used to store system config information in the Apache tree, so it's still relevant. :param loader: Bootloader type. :type loader: str :param interface: Name of the interface. :type interface: str """ if loader is None: loader = self.boot_loader if interface not in self.interfaces: return None if self.name == "default": if loader == "grub": return None return "default" mac = self.get_mac_address(interface) ip = self.get_ip_address(interface) if mac is not None and mac != "": if loader == "grub": return mac.lower() else: return "01-" + "-".join(mac.split(":")).lower() elif ip is not None and ip != "": return utils.get_host_ip(ip) else: return self.name
def to_string(self): buf = "" buf += _("defaults\n") buf += _("kernel options : %s\n") % self.__dict__['kernel_options'] return buf
class Distro(item.Item): """ A cobbler distribution object """ TYPE_NAME = _("distro") COLLECTION_TYPE = "distro" def __init__(self, *args, **kwargs): super(Distro, self).__init__(*args, **kwargs) self.kernel_options = {} self.kernel_options_post = {} self.ks_meta = {} self.source_repos = [] self.fetchable_files = {} self.boot_files = {} self.template_files = {} # # override some base class methods first (item.Item) # def make_clone(self): _dict = self.to_dict() cloned = Distro(self.collection_mgr) cloned.from_dict(_dict) return cloned def get_fields(self): """ Return the list of fields and their properties """ return FIELDS def get_parent(self): """ Distros don't have parent objects. """ return None def check_if_valid(self): if self.name is None: raise CX("name is required") if self.kernel is None: raise CX("Error with distro %s - kernel is required" % (self.name)) if self.initrd is None: raise CX("Error with distro %s - initrd is required" % (self.name)) if utils.file_is_remote(self.kernel): if not utils.remote_file_exists(self.kernel): raise CX("Error with distro %s - kernel '%s' not found" % (self.name, self.kernel)) elif not os.path.exists(self.kernel): raise CX("Error with distro %s - kernel not found" % (self.name)) if utils.file_is_remote(self.initrd): if not utils.remote_file_exists(self.initrd): raise CX("Error with distro %s - initrd path not found" % (self.name)) elif not os.path.exists(self.initrd): raise CX("Error with distro %s - initrd path not found" % (self.name)) # # specific methods for item.Distro # def set_kernel(self, kernel): """ Specifies a kernel. The kernel parameter is a full path, a filename in the configured kernel directory (set in /etc/cobbler.conf) or a directory path that would contain a selectable kernel. Kernel naming conventions are checked, see docs in the utils module for find_kernel. """ if kernel is None or kernel == "": raise CX("kernel not specified") if utils.find_kernel(kernel): self.kernel = kernel return raise CX("kernel not found: %s" % kernel) def set_tree_build_time(self, datestamp): """ Sets the import time of the distro. If not imported, this field is not meaningful. """ self.tree_build_time = float(datestamp) def set_breed(self, breed): return utils.set_breed(self, breed) def set_os_version(self, os_version): return utils.set_os_version(self, os_version) def set_initrd(self, initrd): """ Specifies an initrd image. Path search works as in set_kernel. File must be named appropriately. """ if initrd is None or initrd == "": raise CX("initrd not specified") if utils.find_initrd(initrd): self.initrd = initrd return raise CX(_("initrd not found")) def set_source_repos(self, repos): """ A list of http:// URLs on the cobbler server that point to yum configuration files that can be used to install core packages. Use by cobbler import only. """ self.source_repos = repos def set_arch(self, arch): """ The field is mainly relevant to PXE provisioning. Using an alternative distro type allows for dhcpd.conf templating to "do the right thing" with those systems -- this also relates to bootloader configuration files which have different syntax for different distro types (because of the bootloaders). This field is named "arch" because mainly on Linux, we only care about the architecture, though if (in the future) new provisioning types are added, an arch value might be something like "bsd_x86". """ return utils.set_arch(self, arch)
class Repo(item.Item): """ A Cobbler repo object. """ TYPE_NAME = _("repo") COLLECTION_TYPE = "repo" def __init__(self, *args, **kwargs): super(Repo, self).__init__(*args, **kwargs) self.breed = None self.arch = None self.environment = {} self.yumopts = {} self.rsyncopts = {} # # override some base class methods first (item.Item) # def make_clone(self): """ Clone this file object. Please manually adjust all value yourself to make the cloned object unique. :return: The cloned instance of this object. """ _dict = self.to_dict() cloned = Repo(self.collection_mgr) cloned.from_dict(_dict) return cloned def get_fields(self): """ Return all fields which this class has with its current values. :return: This is a list with lists. """ return FIELDS def get_parent(self): """ Currently the Cobbler object space does not support subobjects of this object as it is conceptually not useful. """ return None def check_if_valid(self): """ Checks if the object is valid. Currently checks for name and mirror to be present. """ if self.name is None: raise CX("name is required") if self.mirror is None: raise CX("Error with repo %s - mirror is required" % (self.name)) # # specific methods for item.File # def _guess_breed(self): """ Guess the breed of a mirror. """ # backwards compatibility if not self.breed: if self.mirror.startswith("http://") or self.mirror.startswith("https://") or self.mirror.startswith("ftp://"): self.set_breed("yum") elif self.mirror.startswith("rhn://"): self.set_breed("rhn") else: self.set_breed("rsync") def set_mirror(self, mirror): """ A repo is (initially, as in right now) is something that can be rsynced. reposync/repotrack integration over HTTP might come later. :param mirror: The mirror URI. """ self.mirror = mirror if not self.arch: if mirror.find("x86_64") != -1: self.set_arch("x86_64") elif mirror.find("x86") != -1 or mirror.find("i386") != -1: self.set_arch("i386") self._guess_breed() def set_keep_updated(self, keep_updated): """ This allows the user to disable updates to a particular repo for whatever reason. :param keep_updated: This may be a bool-like value if the repository shall be keept up to date or not. """ self.keep_updated = utils.input_boolean(keep_updated) def set_yumopts(self, options): """ Kernel options are a space delimited list. :param options: Something like 'a=b c=d e=f g h i=j' or a dictionary. """ (success, value) = utils.input_string_or_dict(options, allow_multiples=False) if not success: raise CX(_("invalid yum options")) else: self.yumopts = value def set_rsyncopts(self, options): """ rsync options are a space delimited list :param options: Something like '-a -S -H -v' """ (success, value) = utils.input_string_or_dict(options, allow_multiples=False) if not success: raise CX(_("invalid rsync options")) else: self.rsyncopts = value def set_environment(self, options): """ Yum can take options from the environment. This puts them there before each reposync. :param options: These are environment variables which are set before each reposync. """ (success, value) = utils.input_string_or_dict(options, allow_multiples=False) if not success: raise CX(_("invalid environment options")) else: self.environment = value def set_priority(self, priority): """ Set the priority of the repository. Only works if host is using priorities plugin for yum. :param priority: Must be a value between 1 and 99. 1 is the highest whereas 99 is the default and lowest. """ try: priority = int(str(priority)) except: raise CX(_("invalid priority level: %s") % priority) self.priority = priority def set_rpm_list(self, rpms): """ Rather than mirroring the entire contents of a repository (Fedora Extras, for instance, contains games, and we probably don't want those), make it possible to list the packages one wants out of those repos, so only those packages and deps can be mirrored. :param rpms: The rpm to mirror. This may be a string or list. """ self.rpm_list = utils.input_string_or_list(rpms) def set_createrepo_flags(self, createrepo_flags): """ Flags passed to createrepo when it is called. Common flags to use would be ``-c cache`` or ``-g comps.xml`` to generate group information. :param createrepo_flags: The createrepo flags which are passed additionally to the default ones. """ if createrepo_flags is None: createrepo_flags = "" self.createrepo_flags = createrepo_flags def set_breed(self, breed): """ Setter for the operating system breed. :param breed: The new breed to set. If this argument evaluates to false then nothing will be done. """ if breed: return utils.set_repo_breed(self, breed) def set_os_version(self, os_version): """ Setter for the operating system version. :param os_version: The new operating system version. If this argument evaluates to false then nothing will be done. """ if os_version: return utils.set_repo_os_version(self, os_version) def set_arch(self, arch): """ Override the arch used for reposync :param arch: The new arch which will be used. """ return utils.set_arch(self, arch, repo=True) def set_mirror_locally(self, value): """ Setter for the local mirror property. :param value: The new value for ``mirror_locally``. """ self.mirror_locally = utils.input_boolean(value) def set_apt_components(self, value): """ Setter for the apt command property. :param value: The new value for ``apt_components``. """ self.apt_components = utils.input_string_or_list(value) def set_apt_dists(self, value): """ Setter for the apt dists. :param value: The new value for ``apt_dists``. :return: ``True`` if everything went correctly. """ self.apt_dists = utils.input_string_or_list(value) return True def set_proxy(self, value): """ Setter for the proxy setting of the repository. :param value: The new proxy which will be used for the repository. :return: ``True`` if this succeeds. """ self.proxy = value return True
def write_dhcp_file(self): """ DHCP files are written when manage_dhcp is set in /etc/cobbler/settings. """ template_file = "/etc/cobbler/dhcp.template" blender_cache = {} try: f2 = open(template_file, "r") except: raise CX(_("error reading template: %s") % template_file) template_data = "" template_data = f2.read() f2.close() # use a simple counter for generating generic names where a hostname # is not available counter = 0 # we used to just loop through each system, but now we must loop # through each network interface of each system. dhcp_tags = {"default": {}} yaboot = "/yaboot" # FIXME: ding should evolve into the new dhcp_tags dict ding = {} ignore_macs = [] for system in self.systems: if not system.is_management_supported(cidr_ok=False): continue profile = system.get_conceptual_parent() distro = profile.get_conceptual_parent() # if distro is None then the profile is really an image record for (name, system_interface) in list(system.interfaces.items()): # We make a copy because we may modify it before adding it to the dhcp_tags # and we don't want to affect the master copy. interface = copy.deepcopy(system_interface) if interface["if_gateway"]: interface["gateway"] = interface["if_gateway"] else: interface["gateway"] = system.gateway mac = interface["mac_address"] if interface["interface_type"] in ("bond_slave", "bridge_slave", "bonded_bridge_slave"): if interface["interface_master"] not in system.interfaces: # Can't write DHCP entry; master interface does not exist continue # We may have multiple bonded interfaces, so we need a composite index into ding. name_master = "%s-%s" % (system.name, interface["interface_master"]) if name_master not in ding: ding[name_master] = {interface["interface_master"]: []} if len(ding[name_master][ interface["interface_master"]]) == 0: ding[name_master][ interface["interface_master"]].append(mac) else: ignore_macs.append(mac) ip = system.interfaces[ interface["interface_master"]]["ip_address"] netmask = system.interfaces[ interface["interface_master"]]["netmask"] dhcp_tag = system.interfaces[ interface["interface_master"]]["dhcp_tag"] host = system.interfaces[ interface["interface_master"]]["dns_name"] if ip is None or ip == "": for (nam2, int2) in list(system.interfaces.items()): if (nam2.startswith(interface["interface_master"] + ".") and int2["ip_address"] is not None and int2["ip_address"] != ""): ip = int2["ip_address"] break interface["ip_address"] = ip interface["netmask"] = netmask else: ip = interface["ip_address"] netmask = interface["netmask"] dhcp_tag = interface["dhcp_tag"] host = interface["dns_name"] if distro is not None: interface["distro"] = distro.to_dict() if mac is None or mac == "": # can't write a DHCP entry for this system continue counter = counter + 1 # the label the entry after the hostname if possible if host is not None and host != "": if name != "eth0": interface["name"] = "%s-%s" % (host, name) else: interface["name"] = "%s" % (host) else: interface["name"] = "generic%d" % counter # add references to the system, profile, and distro # for use in the template if system.name in blender_cache: blended_system = blender_cache[system.name] else: blended_system = utils.blender(self.api, False, system) blender_cache[system.name] = blended_system interface["next_server"] = blended_system["next_server"] interface["netboot_enabled"] = blended_system[ "netboot_enabled"] interface["hostname"] = blended_system["hostname"] interface["owner"] = blended_system["name"] interface["enable_gpxe"] = blended_system["enable_gpxe"] interface["name_servers"] = blended_system["name_servers"] interface["mgmt_parameters"] = blended_system[ "mgmt_parameters"] # Explicitly declare filename for other (non x86) archs as in DHCP discover # package mostly the architecture cannot be differed due to missing bits... if distro is not None and not interface.get("filename"): if distro.arch == "ppc" or distro.arch == "ppc64": interface["filename"] = yaboot elif distro.arch == "ppc64le": interface["filename"] = "grub/grub.ppc64le" elif distro.arch == "aarch64": interface["filename"] = "grub/grubaa64.efi" if not self.settings.always_write_dhcp_entries: if not interface["netboot_enabled"] and interface['static']: continue if dhcp_tag == "": dhcp_tag = blended_system.get("dhcp_tag", "") if dhcp_tag == "": dhcp_tag = "default" if dhcp_tag not in dhcp_tags: dhcp_tags[dhcp_tag] = {mac: interface} else: dhcp_tags[dhcp_tag][mac] = interface # remove macs from redundant slave interfaces from dhcp_tags # otherwise you get duplicate ip's in the installer for dt in list(dhcp_tags.keys()): for m in list(dhcp_tags[dt].keys()): if m in ignore_macs: del dhcp_tags[dt][m] # we are now done with the looping through each interface of each system metadata = { "date": time.asctime(time.gmtime()), "cobbler_server": "%s:%s" % (self.settings.server, self.settings.http_port), "next_server": self.settings.next_server, "yaboot": yaboot, "dhcp_tags": dhcp_tags } if self.logger is not None: self.logger.info("generating %s" % self.settings_file) self.templar.render(template_data, metadata, self.settings_file, None)
class Repo(item.Item): """ A Cobbler repo object. """ TYPE_NAME = _("repo") COLLECTION_TYPE = "repo" def __init__(self, *args, **kwargs): super(Repo, self).__init__(*args, **kwargs) self.breed = None self.arch = None self.environment = None self.yumopts = None # # override some base class methods first (item.Item) # def make_clone(self): _dict = self.to_dict() cloned = Repo(self.collection_mgr) cloned.from_dict(_dict) return cloned def get_fields(self): return FIELDS def get_parent(self): """ currently the Cobbler object space does not support subobjects of this object as it is conceptually not useful. """ return None def check_if_valid(self): if self.name is None: raise CX("name is required") if self.mirror is None: raise CX("Error with repo %s - mirror is required" % (self.name)) # # specific methods for item.File # def _guess_breed(self): # backwards compatibility if (self.breed == "" or self.breed is None): if self.mirror.startswith("http://") or self.mirror.startswith( "ftp://"): self.set_breed("yum") elif self.mirror.startswith("rhn://"): self.set_breed("rhn") else: self.set_breed("rsync") def set_mirror(self, mirror): """ A repo is (initially, as in right now) is something that can be rsynced. reposync/repotrack integration over HTTP might come later. """ self.mirror = mirror if self.arch is None or self.arch == "": if mirror.find("x86_64") != -1: self.set_arch("x86_64") elif mirror.find("x86") != -1 or mirror.find("i386") != -1: self.set_arch("i386") self._guess_breed() def set_keep_updated(self, keep_updated): """ This allows the user to disable updates to a particular repo for whatever reason. """ self.keep_updated = utils.input_boolean(keep_updated) def set_yumopts(self, options): """ Kernel options are a space delimited list, like 'a=b c=d e=f g h i=j' or a dictionary. """ (success, value) = utils.input_string_or_dict(options, allow_multiples=False) if not success: raise CX(_("invalid yum options")) else: self.yumopts = value def set_environment(self, options): """ Yum can take options from the environment. This puts them there before each reposync. """ (success, value) = utils.input_string_or_dict(options, allow_multiples=False) if not success: raise CX(_("invalid environment options")) else: self.environment = value def set_priority(self, priority): """ Set the priority of the repository. 1= highest, 99=default Only works if host is using priorities plugin for yum. """ try: priority = int(str(priority)) except: raise CX(_("invalid priority level: %s") % priority) self.priority = priority def set_rpm_list(self, rpms): """ Rather than mirroring the entire contents of a repository (Fedora Extras, for instance, contains games, and we probably don't want those), make it possible to list the packages one wants out of those repos, so only those packages + deps can be mirrored. """ self.rpm_list = utils.input_string_or_list(rpms) def set_createrepo_flags(self, createrepo_flags): """ Flags passed to createrepo when it is called. Common flags to use would be -c cache or -g comps.xml to generate group information. """ if createrepo_flags is None: createrepo_flags = "" self.createrepo_flags = createrepo_flags def set_breed(self, breed): if breed: return utils.set_repo_breed(self, breed) def set_os_version(self, os_version): if os_version: return utils.set_repo_os_version(self, os_version) def set_arch(self, arch): """ Override the arch used for reposync """ return utils.set_arch(self, arch, repo=True) def set_mirror_locally(self, value): self.mirror_locally = utils.input_boolean(value) def set_apt_components(self, value): self.apt_components = utils.input_string_or_list(value) def set_apt_dists(self, value): self.apt_dists = utils.input_string_or_list(value)
def set_boot_loader(self, name): if name not in utils.get_supported_system_boot_loaders(): raise CX(_("Invalid boot loader name: %s" % name)) self.boot_loader = name
class Mgmtclass(item.Item): TYPE_NAME = _("mgmtclass") COLLECTION_TYPE = "mgmtclass" def __init__(self, *args, **kwargs): super(Mgmtclass, self).__init__(*args, **kwargs) self.params = {} # # override some base class methods first (item.Item) # def make_clone(self): """ Clone this file object. Please manually adjust all value yourself to make the cloned object unique. :return: The cloned instance of this object. """ _dict = self.to_dict() cloned = Mgmtclass(self.collection_mgr) cloned.from_dict(_dict) return cloned def get_fields(self): """ Return all fields which this class has with it's current values. :return: This is a list with lists. """ return FIELDS def check_if_valid(self): """ Check if this object is in a valid state. This currently checks only if the name is present. """ if not self.name: raise CX("name is required") # # specific methods for item.Mgmtclass # def set_packages(self, packages): """ Setter for the packages of the managementclass. :param packages: A string or list which contains the new packages. """ self.packages = utils.input_string_or_list(packages) def set_files(self, files): """ Setter for the files of the object. :param files: A string or list which contains the new files. """ self.files = utils.input_string_or_list(files) def set_params(self, params): """ Setter for the params of the managementclass. :param params: The new params for the object. """ (success, value) = utils.input_string_or_dict(params, allow_multiples=True) if not success: raise CX(_("invalid parameters")) else: self.params = value def set_is_definition(self, isdef): """ Setter for property ``is_defintion``. :param isdef: The new value for the property. """ self.is_definition = utils.input_boolean(isdef) def set_class_name(self, name): """ Setter for the name of the managementclass. :param name: The new name of the class. This must not contain "_", "-", ".", ":" or "+". :type name: str """ if not isinstance(name, str) and not isinstance(name, oldstr): raise CX(_("class name must be a string")) for x in name: if not x.isalnum() and x not in ["_", "-", ".", ":", "+"]: raise CX(_("invalid characters in class name: '%s'" % name)) self.class_name = name
class System(item.Item): """ A Cobbler system object. """ TYPE_NAME = _("system") COLLECTION_TYPE = "system" def __init__(self, *args, **kwargs): super(System, self).__init__(*args, **kwargs) self.interfaces = dict() self.kernel_options = {} self.kernel_options_post = {} self.ks_meta = {} self.fetchable_files = {} self.boot_files = {} self.template_files = {} # # override some base class methods first (item.Item) # def get_fields(self): return FIELDS def make_clone(self): ds = self.to_datastruct() cloned = System(self.config) cloned.from_datastruct(ds) return cloned def from_datastruct(self, seed_data): # FIXME: most definitely doesn't grok interfaces yet. return utils.from_datastruct_from_fields(self, seed_data, FIELDS) def get_parent(self): """ Return object next highest up the tree. """ if (self.parent is None or self.parent == '') and self.profile: return self.config.profiles().find(name=self.profile) elif (self.parent is None or self.parent == '') and self.image: return self.config.images().find(name=self.image) else: return self.config.systems().find(name=self.parent) def check_if_valid(self): if self.name is None or self.name == "": raise CX("name is required") if self.profile is None or self.profile == "": if self.image is None or self.image == "": raise CX("Error with system %s - profile or image is required" % (self.name)) # # specific methods for item.Distro # def __get_interface(self, name): if name == "" and len(self.interfaces.keys()) == 0: raise CX(_("No interfaces defined. Please use --interface <interface_name>")) elif name == "" and len(self.interfaces.keys()) == 1: name = self.interfaces.keys()[0] elif name == "" and len(self.interfaces.keys()) > 1: raise CX(_("Multiple interfaces defined. Please use --interface <interface_name>")) elif name not in self.interfaces: self.interfaces[name] = { "mac_address": "", "mtu": "", "ip_address": "", "dhcp_tag": "", "netmask": "", "if_gateway": "", "virt_bridge": "", "static": False, "interface_type": "", "interface_master": "", "bonding_opts": "", "bridge_opts": "", "management": False, "dns_name": "", "static_routes": [], "ipv6_address": "", "ipv6_prefix": "", "ipv6_secondaries": [], "ipv6_mtu": "", "ipv6_static_routes": [], "ipv6_default_gateway": "", "cnames": [], } return self.interfaces[name] def delete_interface(self, name): """ Used to remove an interface. """ if name in self.interfaces and len(self.interfaces) > 1: del self.interfaces[name] else: if name not in self.interfaces: # no interface here to delete pass else: raise CX(_("At least one interface needs to be defined.")) return True def rename_interface(self, names): """ Used to rename an interface. """ (name, newname) = names if name not in self.interfaces: raise CX(_("Interface %s does not exist" % name)) if newname in self.interfaces: raise CX(_("Interface %s already exists" % newname)) else: self.interfaces[newname] = self.interfaces[name] del self.interfaces[name] return True def set_redhat_management_key(self, key): return utils.set_redhat_management_key(self, key) def set_redhat_management_server(self, server): return utils.set_redhat_management_server(self, server) def set_server(self, server): """ If a system can't reach the boot server at the value configured in settings because it doesn't have the same name on it's subnet this is there for an override. """ if server is None or server == "": server = "<<inherit>>" self.server = server return True def set_proxy(self, proxy): if proxy is None or proxy == "": proxy = "<<inherit>>" self.proxy = proxy return True def get_mac_address(self, interface): """ Get the mac address, which may be implicit in the object name or explicit with --mac-address. Use the explicit location first. """ intf = self.__get_interface(interface) if intf["mac_address"] != "": return intf["mac_address"].strip() else: return None def get_ip_address(self, interface): """ Get the IP address, which may be implicit in the object name or explict with --ip-address. Use the explicit location first. """ intf = self.__get_interface(interface) if intf["ip_address"] != "": return intf["ip_address"].strip() else: return "" def is_management_supported(self, cidr_ok=True): """ Can only add system PXE records if a MAC or IP address is available, else it's a koan only record. """ if self.name == "default": return True for (name, x) in self.interfaces.iteritems(): mac = x.get("mac_address", None) ip = x.get("ip_address", None) if ip is not None and not cidr_ok and ip.find("/") != -1: # ip is in CIDR notation return False if mac is not None or ip is not None: # has ip and/or mac return True return False def set_dhcp_tag(self, dhcp_tag, interface): intf = self.__get_interface(interface) intf["dhcp_tag"] = dhcp_tag return True def set_dns_name(self, dns_name, interface): """ Set DNS name for interface. @param: str dns_name (dns name) @param: str interface (interface name) @returns: True or CX """ dns_name = validate.hostname(dns_name) if dns_name != "" and utils.input_boolean(self.config._settings.allow_duplicate_hostnames) is False: matched = self.config.api.find_items("system", {"dns_name": dns_name}) for x in matched: if x.name != self.name: raise CX("DNS name duplicated: %s" % dns_name) intf = self.__get_interface(interface) intf["dns_name"] = dns_name return True def set_cnames(self, cnames, interface): intf = self.__get_interface(interface) data = utils.input_string_or_list(cnames) intf["cnames"] = data return True def set_static_routes(self, routes, interface): intf = self.__get_interface(interface) data = utils.input_string_or_list(routes) intf["static_routes"] = data return True def set_hostname(self, hostname): """ Set hostname. @param: str hostname (hostname for system) @returns: True or CX """ self.hostname = validate.hostname(hostname) return True def set_status(self, status): self.status = status return True def set_static(self, truthiness, interface): intf = self.__get_interface(interface) intf["static"] = utils.input_boolean(truthiness) return True def set_management(self, truthiness, interface): intf = self.__get_interface(interface) intf["management"] = utils.input_boolean(truthiness) return True def set_ip_address(self, address, interface): """ Set IPv4 address on interface. @param: str address (ip address) @param: str interface (interface name) @returns: True or CX """ address = validate.ipv4_address(address) if address != "" and utils.input_boolean(self.config._settings.allow_duplicate_ips) is False: matched = self.config.api.find_items("system", {"ip_address": address}) for x in matched: if x.name != self.name: raise CX("IP address duplicated: %s" % address) intf = self.__get_interface(interface) intf["ip_address"] = address return True def set_mac_address(self, address, interface): """ Set mc address on interface. @param: str address (mac address) @param: str interface (interface name) @returns: True or CX """ address = validate.mac_address(address) if address == "random": address = utils.get_random_mac(self.config.api) if address != "" and utils.input_boolean(self.config._settings.allow_duplicate_macs) is False: matched = self.config.api.find_items("system", {"mac_address": address}) for x in matched: if x.name != self.name: raise CX("MAC address duplicated: %s" % address) intf = self.__get_interface(interface) intf["mac_address"] = address.strip() return True def set_gateway(self, gateway): """ Set a gateway IPv4 address. @param: str gateway (ip address) @returns: True or CX """ self.gateway = validate.ipv4_address(gateway) return True def set_name_servers(self, data): """ Set the DNS servers. @param: str/list data (string or list of nameservers) @returns: True or CX """ self.name_servers = validate.name_servers(data) return True def set_name_servers_search(self, data): """ Set the DNS search paths. @param: str/list data (string or list of search domains) @returns: True or CX """ self.name_servers_search = validate.name_servers_search(data) return True def set_netmask(self, netmask, interface): intf = self.__get_interface(interface) intf["netmask"] = netmask return True def set_if_gateway(self, gateway, interface): intf = self.__get_interface(interface) if gateway == "" or utils.is_ip(gateway): intf["if_gateway"] = gateway return True raise CX(_("invalid gateway: %s" % gateway)) def set_virt_bridge(self, bridge, interface): if bridge == "": bridge = self.settings.default_virt_bridge intf = self.__get_interface(interface) intf["virt_bridge"] = bridge return True def set_interface_type(self, type, interface): interface_types = ["bridge", "bridge_slave", "bond", "bond_slave", "bonded_bridge_slave", "na", ""] if type not in interface_types: raise CX(_("interface type value must be one of: %s or blank" % ",".join(interface_types))) if type == "na": type = "" intf = self.__get_interface(interface) intf["interface_type"] = type return True def set_interface_master(self, interface_master, interface): intf = self.__get_interface(interface) intf["interface_master"] = interface_master return True def set_bonding_opts(self, bonding_opts, interface): intf = self.__get_interface(interface) intf["bonding_opts"] = bonding_opts return True def set_bridge_opts(self, bridge_opts, interface): intf = self.__get_interface(interface) intf["bridge_opts"] = bridge_opts return True def set_ipv6_autoconfiguration(self, truthiness): self.ipv6_autoconfiguration = utils.input_boolean(truthiness) return True def set_ipv6_default_device(self, interface_name): if interface_name is None: interface_name = "" self.ipv6_default_device = interface_name return True def set_ipv6_address(self, address, interface): """ Assign a IP or hostname in DHCP when this MAC boots. Only works if manage_dhcp is set in /etc/cobbler/settings """ intf = self.__get_interface(interface) if address == "" or utils.is_ip(address): intf["ipv6_address"] = address.strip() return True raise CX(_("invalid format for IPv6 IP address (%s)") % address) def set_ipv6_prefix(self, prefix, interface): """ Assign a IPv6 prefix """ intf = self.__get_interface(interface) intf["ipv6_prefix"] = prefix.strip() return True def set_ipv6_secondaries(self, addresses, interface): intf = self.__get_interface(interface) data = utils.input_string_or_list(addresses) secondaries = [] for address in data: if address == "" or utils.is_ip(address): secondaries.append(address) else: raise CX(_("invalid format for IPv6 IP address (%s)") % address) intf["ipv6_secondaries"] = secondaries return True def set_ipv6_default_gateway(self, address, interface): intf = self.__get_interface(interface) if address == "" or utils.is_ip(address): intf["ipv6_default_gateway"] = address.strip() return True raise CX(_("invalid format for IPv6 IP address (%s)") % address) def set_ipv6_static_routes(self, routes, interface): intf = self.__get_interface(interface) data = utils.input_string_or_list(routes) intf["ipv6_static_routes"] = data return True def set_ipv6_mtu(self, mtu, interface): intf = self.__get_interface(interface) intf["ipv6_mtu"] = mtu return True def set_mtu(self, mtu, interface): intf = self.__get_interface(interface) intf["mtu"] = mtu return True def set_enable_gpxe(self, enable_gpxe): """ Sets whether or not the system will use gPXE for booting. """ self.enable_gpxe = utils.input_boolean(enable_gpxe) return True def set_profile(self, profile_name): """ Set the system to use a certain named profile. The profile must have already been loaded into the Profiles collection. """ old_parent = self.get_parent() if profile_name in ["delete", "None", "~", ""] or profile_name is None: self.profile = "" if isinstance(old_parent, item.Item): old_parent.children.pop(self.name, 'pass') return True self.image = "" # mutual exclusion rule p = self.config.profiles().find(name=profile_name) if p is not None: self.profile = profile_name self.depth = p.depth + 1 # subprofiles have varying depths. if isinstance(old_parent, item.Item): old_parent.children.pop(self.name, 'pass') new_parent = self.get_parent() if isinstance(new_parent, item.Item): new_parent.children[self.name] = self return True raise CX(_("invalid profile name: %s") % profile_name) def set_image(self, image_name): """ Set the system to use a certain named image. Works like set_profile but cannot be used at the same time. It's one or the other. """ old_parent = self.get_parent() if image_name in ["delete", "None", "~", ""] or image_name is None: self.image = "" if isinstance(old_parent, item.Item): old_parent.children.pop(self.name, 'pass') return True self.profile = "" # mutual exclusion rule img = self.config.images().find(name=image_name) if img is not None: self.image = image_name self.depth = img.depth + 1 if isinstance(old_parent, item.Item): old_parent.children.pop(self.name, 'pass') new_parent = self.get_parent() if isinstance(new_parent, item.Item): new_parent.children[self.name] = self return True raise CX(_("invalid image name (%s)") % image_name) def set_virt_cpus(self, num): return utils.set_virt_cpus(self, num) def set_virt_file_size(self, num): return utils.set_virt_file_size(self, num) def set_virt_disk_driver(self, driver): return utils.set_virt_disk_driver(self, driver) def set_virt_auto_boot(self, num): return utils.set_virt_auto_boot(self, num) def set_virt_pxe_boot(self, num): return utils.set_virt_pxe_boot(self, num) def set_virt_ram(self, num): return utils.set_virt_ram(self, num) def set_virt_type(self, vtype): return utils.set_virt_type(self, vtype) def set_virt_path(self, path): return utils.set_virt_path(self, path, for_system=True) def set_netboot_enabled(self, netboot_enabled): """ If true, allows per-system PXE files to be generated on sync (or add). If false, these files are not generated, thus eliminating the potential for an infinite install loop when systems are set to PXE boot first in the boot order. In general, users who are PXE booting first in the boot order won't create system definitions, so this feature primarily comes into play for programmatic users of the API, who want to initially create a system with netboot enabled and then disable it after the system installs, as triggered by some action in kickstart %post. For this reason, this option is not surfaced in the CLI, output, or documentation (yet). Use of this option does not affect the ability to use PXE menus. If an admin has machines set up to PXE only after local boot fails, this option isn't even relevant. """ self.netboot_enabled = utils.input_boolean(netboot_enabled) return True def set_kickstart(self, kickstart): """ Set the kickstart path, this must be a local file. @param: str kickstart path to a local kickstart file @returns: True or CX """ self.kickstart = validate.kickstart_file_path(kickstart) return True def set_power_type(self, power_type): # FIXME: modularize this better if power_type is None: power_type = "" choices = utils.get_power_types() if not choices: raise CX("you need to have fence-agents installed") if power_type not in choices: raise CX("power management type must be one of: %s" % ",".join(choices)) self.power_type = power_type return True def set_power_user(self, power_user): if power_user is None: power_user = "" utils.safe_filter(power_user) self.power_user = power_user return True def set_power_pass(self, power_pass): if power_pass is None: power_pass = "" utils.safe_filter(power_pass) self.power_pass = power_pass return True def set_power_address(self, power_address): if power_address is None: power_address = "" utils.safe_filter(power_address) self.power_address = power_address return True def set_power_id(self, power_id): if power_id is None: power_id = "" utils.safe_filter(power_id) self.power_id = power_id return True def modify_interface(self, hash): """ Used by the WUI to modify an interface more-efficiently """ for (key, value) in hash.iteritems(): (field, interface) = key.split("-", 1) field = field.replace("_", "").replace("-", "") if field == "macaddress": self.set_mac_address(value, interface) if field == "mtu": self.set_mtu(value, interface) if field == "ipaddress": self.set_ip_address(value, interface) if field == "dnsname": self.set_dns_name(value, interface) if field == "static": self.set_static(value, interface) if field == "dhcptag": self.set_dhcp_tag(value, interface) if field == "netmask": self.set_netmask(value, interface) if field == "ifgateway": self.set_if_gateway(value, interface) if field == "virtbridge": self.set_virt_bridge(value, interface) if field == "interfacetype": self.set_interface_type(value, interface) if field == "interfacemaster": self.set_interface_master(value, interface) if field == "bondingopts": self.set_bonding_opts(value, interface) if field == "bridgeopts": self.set_bridge_opts(value, interface) if field == "management": self.set_management(value, interface) if field == "staticroutes": self.set_static_routes(value, interface) if field == "ipv6address": self.set_ipv6_address(value, interface) if field == "ipv6prefix": self.set_ipv6_prefix(value, interface) if field == "ipv6secondaries": self.set_ipv6_secondaries(value, interface) if field == "ipv6mtu": self.set_ipv6_mtu(value, interface) if field == "ipv6staticroutes": self.set_ipv6_static_routes(value, interface) if field == "ipv6defaultgateway": self.set_ipv6_default_gateway(value, interface) if field == "cnames": self.set_cnames(value, interface) return True def set_monit_enabled(self, monit_enabled): """ If true, allows per-system to start Monit to monitor system services such as apache. If monit is not running it will start the service. If false, no management of monit will take place. If monit is not running it will not be started. If monit is running it will not be stopped or restarted. """ self.monit_enabled = utils.input_boolean(monit_enabled) return True def set_ldap_enabled(self, ldap_enabled): self.ldap_enabled = utils.input_boolean(ldap_enabled) return True def set_repos_enabled(self, repos_enabled): self.repos_enabled = utils.input_boolean(repos_enabled) return True def set_ldap_type(self, ldap_type): if ldap_type is None: ldap_type = "" ldap_type = ldap_type.lower() self.ldap_type = ldap_type return True
class Image(item.Item): """ A Cobbler Image. Tracks a virtual or physical image, as opposed to a answer file (autoinst) led installation. """ TYPE_NAME = _("image") COLLECTION_TYPE = "image" # # override some base class methods first (item.Item) # def make_clone(self): _dict = self.to_dict() cloned = Image(self.collection_mgr) cloned.from_dict(_dict) return cloned def get_fields(self): return FIELDS def get_parent(self): """ Images have no parent object. """ return None # # specific methods for item.Image # def set_arch(self, arch): """ The field is mainly relevant to PXE provisioning. see comments for set_arch in item_distro.py, this works the same. """ return utils.set_arch(self, arch) def set_autoinstall(self, autoinstall): """ Set the automatic installation file path, this must be a local file. It may not make sense for images to have automatic installation templates. It really doesn't. However if the image type is 'iso' koan can create a virtual floppy and shove an answer file on it, to script an installation. This may not be a automatic installation template per se, it might be a Windows answer file (SIF) etc. @param str local automatic installation template file path """ autoinstall_mgr = autoinstall_manager.AutoInstallationManager(self.collection_mgr) self.autoinstall = autoinstall_mgr.validate_autoinstall_template_file_path(autoinstall) def set_file(self, filename): """ Stores the image location. This should be accessible on all nodes that need to access it. Format: can be one of the following: * username:password@hostname:/path/to/the/filename.ext * username@hostname:/path/to/the/filename.ext * hostname:/path/to/the/filename.ext * /path/to/the/filename.ext """ uri = "" auth = hostname = path = "" # validate file location format if filename.find("://") != -1: raise CX("Invalid image file path location, it should not contain a protocol") uri = filename if filename.find("@") != -1: auth, filename = filename.split("@") # extract the hostname # 1. if we have a colon, then everything before it is a hostname # 2. if we don't have a colon, there is no hostname if filename.find(":") != -1: hostname, filename = filename.split(":") elif filename[0] != '/': raise CX(_("invalid file: %s" % filename)) # raise an exception if we don't have a valid path if len(filename) > 0 and filename[0] != '/': raise CX(_("file contains an invalid path: %s" % filename)) if filename.find("/") != -1: path, filename = filename.rsplit("/", 1) if len(filename) == 0: raise CX(_("missing filename")) if len(auth) > 0 and len(hostname) == 0: raise CX(_("a hostname must be specified with authentication details")) self.file = uri def set_os_version(self, os_version): return utils.set_os_version(self, os_version) def set_breed(self, breed): return utils.set_breed(self, breed) def set_image_type(self, image_type): """ Indicates what type of image this is. direct = something like "memdisk", physical only iso = a bootable ISO that pxe's or can be used for virt installs, virtual only virt-clone = a cloned virtual disk (FIXME: not yet supported), virtual only memdisk = hdd image (physical only) """ if image_type not in self.get_valid_image_types(): raise CX(_("image type must be on of the following: %s") % string.join(self.get_valid_image_types(), ", ")) self.image_type = image_type def set_virt_cpus(self, num): return utils.set_virt_cpus(self, num) def set_network_count(self, num): if num is None or num == "": num = 1 try: self.network_count = int(num) except: raise CX("invalid network count (%s)" % num) def set_virt_auto_boot(self, num): return utils.set_virt_auto_boot(self, num) def set_virt_file_size(self, num): return utils.set_virt_file_size(self, num) def set_virt_disk_driver(self, driver): return utils.set_virt_disk_driver(self, driver) def set_virt_ram(self, num): return utils.set_virt_ram(self, num) def set_virt_type(self, vtype): return utils.set_virt_type(self, vtype) def set_virt_bridge(self, vbridge): return utils.set_virt_bridge(self, vbridge) def set_virt_path(self, path): return utils.set_virt_path(self, path) def get_valid_image_types(self): return ["direct", "iso", "memdisk", "virt-clone"]
def set_if_gateway(self, gateway, interface): intf = self.__get_interface(interface) if gateway == "" or utils.is_ip(gateway): intf["if_gateway"] = gateway return True raise CX(_("invalid gateway: %s" % gateway))
class Profile(item.Item): """ A Cobbler profile object. """ TYPE_NAME = _("profile") COLLECTION_TYPE = "profile" def __init__(self, *args, **kwargs): super(Profile, self).__init__(*args, **kwargs) self.kernel_options = {} self.kernel_options_post = {} self.autoinstall_meta = {} self.fetchable_files = {} self.boot_files = {} self.template_files = {} # # override some base class methods first (item.Item) # def make_clone(self): """ Clone this file object. Please manually adjust all value yourself to make the cloned object unique. :return: The cloned instance of this object. """ _dict = self.to_dict() cloned = Profile(self.collection_mgr) cloned.from_dict(_dict) return cloned def get_fields(self): """ Return all fields which this class has with its current values. :return: This is a list with lists. """ return FIELDS def get_parent(self): """ Return object next highest up the tree. """ if not self.parent: if self.distro is None: return None result = self.collection_mgr.distros().find(name=self.distro) else: result = self.collection_mgr.profiles().find(name=self.parent) return result def check_if_valid(self): """ Check if the profile is valid. This checks for an existing name and a distro as a conceptual parent. """ # name validation if not self.name: raise CX("Name is required") # distro validation distro = self.get_conceptual_parent() if distro is None: raise CX("Error with profile %s - distro is required" % (self.name)) # # specific methods for item.Profile # def set_parent(self, parent_name): """ Instead of a ``--distro``, set the parent of this object to another profile and use the values from the parent instead of this one where the values for this profile aren't filled in, and blend them together where they are dictionaries. Basically this enables profile inheritance. To use this, the object MUST have been constructed with ``is_subobject=True`` or the default values for everything will be screwed up and this will likely NOT work. So, API users -- make sure you pass ``is_subobject=True`` into the constructor when using this. :param parent_name: The name of the parent object. """ old_parent = self.get_parent() if isinstance(old_parent, item.Item): old_parent.children.pop(self.name, 'pass') if not parent_name: self.parent = '' return if parent_name == self.name: # check must be done in two places as set_parent could be called before/after # set_name... raise CX(_("self parentage is weird")) found = self.collection_mgr.profiles().find(name=parent_name) if found is None: raise CX( _("profile %s not found, inheritance not possible") % parent_name) self.parent = parent_name self.depth = found.depth + 1 parent = self.get_parent() if isinstance(parent, item.Item): parent.children[self.name] = self def set_distro(self, distro_name): """ Sets the distro. This must be the name of an existing Distro object in the Distros collection. """ d = self.collection_mgr.distros().find(name=distro_name) if d is not None: old_parent = self.get_parent() if isinstance(old_parent, item.Item): old_parent.children.pop(self.name, 'pass') self.distro = distro_name self.depth = d.depth + 1 # reset depth if previously a subprofile and now top-level d.children[self.name] = self return raise CX(_("distribution not found")) def set_name_servers(self, data): """ Set the DNS servers. :param data: string or list of nameservers :returns: True or throws exception :raises CX: If the nameservers are not valid. """ self.name_servers = validate.name_servers(data) def set_name_servers_search(self, data): """ Set the DNS search paths. :param data: string or list of search domains :returns: True or throws exception :raises CX: If the search domains are not valid. """ self.name_servers_search = validate.name_servers_search(data) def set_proxy(self, proxy): """ Setter for the proxy. :param proxy: The new proxy for the profile. """ self.proxy = proxy def set_enable_gpxe(self, enable_gpxe): """ Sets whether or not the profile will use gPXE for booting. :param enable_gpxe: New boolean value for enabling gPXE. """ self.enable_gpxe = utils.input_boolean(enable_gpxe) def set_enable_menu(self, enable_menu): """ Sets whether or not the profile will be listed in the default PXE boot menu. This is pretty forgiving for YAML's sake. :param enable_menu: New boolean value for enabling the menu. """ self.enable_menu = utils.input_boolean(enable_menu) def set_dhcp_tag(self, dhcp_tag): """ Setter for the dhcp tag property. :param dhcp_tag: """ if dhcp_tag is None: dhcp_tag = "" self.dhcp_tag = dhcp_tag def set_server(self, server): """ Setter for the server property. :param server: If this is None or an emtpy string this will be reset to be inherited from the parent object. """ if server in [None, ""]: server = "<<inherit>>" self.server = server def set_next_server(self, server): """ Setter for the next server value. :param server: If this is None or an emtpy string this will be reset to be inherited from the parent object. """ if server in [None, ""]: self.next_server = "<<inherit>>" else: server = server.strip() if server != "<<inherit>>": self.next_server = validate.ipv4_address(server) else: self.next_server = server def set_filename(self, filename): if not filename: self.filename = "<<inherit>>" else: self.filename = filename.strip() def set_autoinstall(self, autoinstall): """ Set the automatic OS installation template file path, this must be a local file. :param autoinstall: local automatic installation template path :type autoinstall: str """ autoinstall_mgr = autoinstall_manager.AutoInstallationManager( self.collection_mgr) self.autoinstall = autoinstall_mgr.validate_autoinstall_template_file_path( autoinstall) def set_virt_auto_boot(self, num): """ Setter for booting a virtual machine automatically. :param num: The new value for whether to enable it or not. """ utils.set_virt_auto_boot(self, num) def set_virt_cpus(self, num): """ Setter for the number of virtual CPU cores to assign to the virtual machine. :param num: The number of cpu cores. """ utils.set_virt_cpus(self, num) def set_virt_file_size(self, num): """ Setter for the size of the virtual image size. :param num: The new size of the image. """ utils.set_virt_file_size(self, num) def set_virt_disk_driver(self, driver): """ Setter for the virtual disk driver that will be used. :param driver: The new driver. """ utils.set_virt_disk_driver(self, driver) def set_virt_ram(self, num): """ Setter for the virtual RAM used for the VM. :param num: The number of RAM to use for the VM. """ utils.set_virt_ram(self, num) def set_virt_type(self, vtype): """ Setter for the virtual machine type. :param vtype: May be on out of "qemu", "kvm", "xenpv", "xenfv", "vmware", "vmwarew", "openvz" or "auto". """ utils.set_virt_type(self, vtype) def set_virt_bridge(self, vbridge): """ Setter for the name of the virtual bridge to use. :param vbridge: The name of the virtual bridge to use. """ utils.set_virt_bridge(self, vbridge) def set_virt_path(self, path): """ Setter of the path to the place where the image will be stored. :param path: The path to where the image will be stored. """ utils.set_virt_path(self, path) def set_repos(self, repos, bypass_check=False): """ Setter of the repositories for the profile. :param repos: The new repositories which will be set. :param bypass_check: If repository checks should be checked or not. """ utils.set_repos(self, repos, bypass_check) def set_redhat_management_key(self, management_key): """ Setter of the redhat management key. :param management_key: The value may be reset by setting it to None. """ if not management_key: self.redhat_management_key = "<<inherit>>" self.redhat_management_key = management_key def get_redhat_management_key(self): """ Getter of the redhat management key of the profile or it's parent. :return: Returns the redhat_management_key of the profile. """ return self.redhat_management_key def get_arch(self): """ Getter of the architecture of the profile or the parent. :return: The architecture. """ parent = self.get_parent() if parent: return parent.get_arch() return None
def set_params(self, params): (success, value) = utils.input_string_or_dict(params, allow_multiples=True) if not success: raise CX(_("invalid parameters")) else: self.params = value
def add(self, ref, save=False, with_copy=False, with_triggers=True, with_sync=True, quick_pxe_update=False, check_for_duplicate_names=False, check_for_duplicate_netinfo=False, logger=None): """ Add an object to the collection, if it's valid. Returns True if the object was added to the collection. Returns False if the object specified by ref deems itself invalid (and therefore won't be added to the collection). with_copy is a bit of a misnomer, but lots of internal add operations can run with "with_copy" as False. True means a real final commit, as if entered from the command line (or basically, by a user). With with_copy as False, the particular add call might just be being run during deserialization, in which case extra semantics around the add don't really apply. So, in that case, don't run any triggers and don't deal with any actual files. """ if ref is None or ref.name is None: return False try: ref.check_if_valid() except CX: return False if ref.uid == '': ref.uid = self.config.generate_uid() if save is True: now = time.time() if ref.ctime == 0: ref.ctime = now ref.mtime = now if self.lite_sync is None: self.lite_sync = action_litesync.BootLiteSync(self.config, logger=logger) # migration path for old API parameter that I've renamed. if with_copy and not save: save = with_copy if not save: # for people that aren't quite aware of the API # if not saving the object, you can't run these features with_triggers = False with_sync = False # Avoid adding objects to the collection # if an object of the same/ip/mac already exists. self.__duplication_checks(ref, check_for_duplicate_names, check_for_duplicate_netinfo) if ref.COLLECTION_TYPE != self.collection_type(): raise CX(_("API error: storing wrong data type in collection")) if not save: # don't need to run triggers, so add it already ... self.lock.acquire() try: self.listing[ref.name.lower()] = ref finally: self.lock.release() # perform filesystem operations if save: # failure of a pre trigger will prevent the object from being added if with_triggers: utils.run_triggers(self.api, ref, "/var/lib/cobbler/triggers/add/%s/pre/*" % self.collection_type(), [], logger) self.lock.acquire() try: self.listing[ref.name.lower()] = ref finally: self.lock.release() # save just this item if possible, if not, save # the whole collection self.config.serialize_item(self, ref) if with_sync: if isinstance(ref, item_system.System): # we don't need openvz containers to be network bootable if ref.virt_type == "openvz": ref.netboot_enabled = False self.lite_sync.add_single_system(ref.name) elif isinstance(ref, item_profile.Profile): # we don't need openvz containers to be network bootable if ref.virt_type == "openvz": ref.enable_menu = 0 self.lite_sync.add_single_profile(ref.name) elif isinstance(ref, item_distro.Distro): self.lite_sync.add_single_distro(ref.name) elif isinstance(ref, item_image.Image): self.lite_sync.add_single_image(ref.name) elif isinstance(ref, item_repo.Repo): pass elif isinstance(ref, item_mgmtclass.Mgmtclass): pass elif isinstance(ref, item_package.Package): pass elif isinstance(ref, item_file.File): pass else: print _("Internal error. Object type not recognized: %s") % type(ref) if not with_sync and quick_pxe_update: if isinstance(ref, item_system.System): self.lite_sync.update_system_netboot_status(ref.name) # save the tree, so if neccessary, scripts can examine it. if with_triggers: utils.run_triggers(self.api, ref, "/var/lib/cobbler/triggers/change/*", [], logger) utils.run_triggers(self.api, ref, "/var/lib/cobbler/triggers/add/%s/post/*" % self.collection_type(), [], logger) # update children cache in parent object parent = ref.get_parent() if parent is not None: parent.children[ref.name] = ref return True
def __write_secondary_conf(self): """ Write out the secondary.conf secondary config file from the template. """ settings_file = self.settings.bind_chroot_path + '/etc/secondary.conf' template_file = "/etc/cobbler/secondary.template" # forward_zones = self.settings.manage_forward_zones # reverse_zones = self.settings.manage_reverse_zones metadata = { 'forward_zones': list(self.__forward_zones().keys()), 'reverse_zones': [], 'zone_include': '' } for zone in metadata['forward_zones']: txt = """ zone "%(zone)s." { type slave; masters { %(master)s; }; file "data/%(zone)s"; }; """ % { 'zone': zone, 'master': self.settings.bind_master } metadata['zone_include'] = metadata['zone_include'] + txt for zone in list(self.__reverse_zones().keys()): # IPv6 zones are : delimited if ":" in zone: # if IPv6, assume xxxx:xxxx:xxxx:xxxx for the zone # 0123456789012345678 long_zone = (self.__expand_IPv6(zone + '::1'))[:19] tokens = list(re.sub(':', '', long_zone)) tokens.reverse() arpa = '.'.join(tokens) + '.ip6.arpa' else: # IPv4 zones split by '.' tokens = zone.split('.') tokens.reverse() arpa = '.'.join(tokens) + '.in-addr.arpa' # metadata['reverse_zones'].append((zone, arpa)) txt = """ zone "%(arpa)s." { type slave; masters { %(master)s; }; file "data/%(zone)s"; }; """ % { 'arpa': arpa, 'zone': zone, 'master': self.settings.bind_master } metadata['zone_include'] = metadata['zone_include'] + txt metadata['bind_master'] = self.settings.bind_master try: f2 = open(template_file, "r") except: raise CX(_("error reading template from file: %s") % template_file) template_data = "" template_data = f2.read() f2.close() if self.logger is not None: self.logger.info("generating %s" % settings_file) self.templar.render(template_data, metadata, settings_file, None)
def check_for_default_password(self, status): default_pass = self.settings.default_password_crypted if default_pass == "$1$mF86/UHC$WvcIcX2t6crBz2onWxyac.": status.append( _("The default password used by the sample templates for newly installed machines (default_password_crypted in /etc/cobbler/settings) is still set to 'cobbler' and should be changed, try: \"openssl passwd -1 -salt 'random-phrase-here' 'your-password-here'\" to generate new one" ))
def remove(self, name, with_delete=True, with_sync=True, with_triggers=True, recursive=True, logger=None): """ Remove element named 'name' from the collection """ # NOTE: with_delete isn't currently meaningful for repos # but is left in for consistancy in the API. Unused. name = name.lower() # first see if any Groups use this distro if not recursive: for v in self.collection_mgr.systems(): if v.image is not None and v.image.lower() == name: raise CX(_("removal would orphan system: %s") % v.name) obj = self.find(name=name) if obj is not None: if recursive: kids = obj.get_children() for k in kids: self.collection_mgr.api.remove_system(k, recursive=True, logger=logger) if with_delete: if with_triggers: utils.run_triggers( self.collection_mgr.api, obj, "/var/lib/cobbler/triggers/delete/image/pre/*", [], logger) if with_sync: lite_sync = action_litesync.CobblerLiteSync( self.collection_mgr, logger=logger) lite_sync.remove_single_image(name) self.lock.acquire() try: del self.listing[name] finally: self.lock.release() self.collection_mgr.serialize_delete(self, obj) if with_delete: if with_triggers: utils.run_triggers( self.collection_mgr.api, obj, "/var/lib/cobbler/triggers/delete/image/post/*", [], logger) utils.run_triggers(self.collection_mgr.api, obj, "/var/lib/cobbler/triggers/change/*", [], logger) return raise CX(_("cannot delete an object that does not exist: %s") % name)
def __write_zone_files(self): """ Write out the forward and reverse zone files for all configured zones """ default_template_file = "/etc/cobbler/zone.template" cobbler_server = self.settings.server # this could be a config option too serial_filename = "/var/lib/cobbler/bind_serial" # need a counter for new bind format serial = time.strftime("%Y%m%d00") try: serialfd = open(serial_filename, "r") old_serial = serialfd.readline() # same date if serial[0:8] == old_serial[0:8]: if int(old_serial[8:10]) < 99: serial = "%s%.2i" % (serial[0:8], int(old_serial[8:10]) + 1) else: pass serialfd.close() except: pass serialfd = open(serial_filename, "w") serialfd.write(serial) serialfd.close() forward = self.__forward_zones() reverse = self.__reverse_zones() try: f2 = open(default_template_file, "r") except: raise CX( _("error reading template from file: %s") % default_template_file) default_template_data = "" default_template_data = f2.read() f2.close() zonefileprefix = self.settings.bind_chroot_path + self.zonefile_base for (zone, hosts) in list(forward.items()): metadata = { 'cobbler_server': cobbler_server, 'serial': serial, 'zonename': zone, 'zonetype': 'forward', 'cname_record': '', 'host_record': '' } if ":" in zone: long_zone = (self.__expand_IPv6(zone + '::1'))[:19] tokens = list(re.sub(':', '', long_zone)) tokens.reverse() zone_origin = '.'.join(tokens) + '.ip6.arpa.' else: zone_origin = '' # grab zone-specific template if it exists try: fd = open('/etc/cobbler/zone_templates/%s' % zone) # If this is an IPv6 zone, set the origin to the zone for this # template if zone_origin: template_data = r"\$ORIGIN " + zone_origin + "\n" + fd.read( ) else: template_data = fd.read() fd.close() except: # If this is an IPv6 zone, set the origin to the zone for this # template if zone_origin: template_data = r"\$ORIGIN " + zone_origin + "\n" + default_template_data else: template_data = default_template_data metadata['cname_record'] = self.__pretty_print_cname_records(hosts) metadata['host_record'] = self.__pretty_print_host_records(hosts) zonefilename = zonefileprefix + zone if self.logger is not None: self.logger.info("generating (forward) %s" % zonefilename) self.templar.render(template_data, metadata, zonefilename, None) for (zone, hosts) in list(reverse.items()): metadata = { 'cobbler_server': cobbler_server, 'serial': serial, 'zonename': zone, 'zonetype': 'reverse', 'cname_record': '', 'host_record': '' } # grab zone-specific template if it exists try: fd = open('/etc/cobbler/zone_templates/%s' % zone) template_data = fd.read() fd.close() except: template_data = default_template_data metadata['cname_record'] = self.__pretty_print_cname_records(hosts) metadata['host_record'] = self.__pretty_print_host_records( hosts, rectype='PTR') zonefilename = zonefileprefix + zone if self.logger is not None: self.logger.info("generating (reverse) %s" % zonefilename) self.templar.render(template_data, metadata, zonefilename, None)
class Profile(item.Item): """ A Cobbler profile object. """ TYPE_NAME = _("profile") COLLECTION_TYPE = "profile" def __init__(self, *args, **kwargs): super(Profile, self).__init__(*args, **kwargs) self.kernel_options = {} self.kernel_options_post = {} self.autoinstall_meta = {} self.fetchable_files = {} self.boot_files = {} self.template_files = {} # # override some base class methods first (item.Item) # def make_clone(self): _dict = self.to_dict() cloned = Profile(self.collection_mgr) cloned.from_dict(_dict) return cloned def get_fields(self): """ Return the list of fields and their properties """ return FIELDS def get_parent(self): """ Return object next highest up the tree. """ if not self.parent: if self.distro is None: return None result = self.collection_mgr.distros().find(name=self.distro) else: result = self.collection_mgr.profiles().find(name=self.parent) return result def check_if_valid(self): # name validation if not self.name: raise CX("Name is required") # distro validation distro = self.get_conceptual_parent() if distro is None: raise CX("Error with profile %s - distro is required" % (self.name)) # # specific methods for item.Profile # def set_parent(self, parent_name): """ Instead of a --distro, set the parent of this object to another profile and use the values from the parent instead of this one where the values for this profile aren't filled in, and blend them together where they are dictionaries. Basically this enables profile inheritance. To use this, the object MUST have been constructed with is_subobject=True or the default values for everything will be screwed up and this will likely NOT work. So, API users -- make sure you pass is_subobject=True into the constructor when using this. """ old_parent = self.get_parent() if isinstance(old_parent, item.Item): old_parent.children.pop(self.name, 'pass') if not parent_name: self.parent = '' return if parent_name == self.name: # check must be done in two places as set_parent could be called before/after # set_name... raise CX(_("self parentage is weird")) found = self.collection_mgr.profiles().find(name=parent_name) if found is None: raise CX( _("profile %s not found, inheritance not possible") % parent_name) self.parent = parent_name self.depth = found.depth + 1 parent = self.get_parent() if isinstance(parent, item.Item): parent.children[self.name] = self def set_distro(self, distro_name): """ Sets the distro. This must be the name of an existing Distro object in the Distros collection. """ d = self.collection_mgr.distros().find(name=distro_name) if d is not None: old_parent = self.get_parent() if isinstance(old_parent, item.Item): old_parent.children.pop(self.name, 'pass') self.distro = distro_name self.depth = d.depth + 1 # reset depth if previously a subprofile and now top-level d.children[self.name] = self return raise CX(_("distribution not found")) def set_name_servers(self, data): """ Set the DNS servers. @param: str/list data (string or list of nameservers) @returns: True or CX """ self.name_servers = validate.name_servers(data) def set_name_servers_search(self, data): """ Set the DNS search paths. @param: str/list data (string or list of search domains) @returns: True or CX """ self.name_servers_search = validate.name_servers_search(data) def set_proxy(self, proxy): self.proxy = proxy def set_enable_gpxe(self, enable_gpxe): """ Sets whether or not the profile will use gPXE for booting. """ self.enable_gpxe = utils.input_boolean(enable_gpxe) def set_enable_menu(self, enable_menu): """ Sets whether or not the profile will be listed in the default PXE boot menu. This is pretty forgiving for YAML's sake. """ self.enable_menu = utils.input_boolean(enable_menu) def set_dhcp_tag(self, dhcp_tag): if dhcp_tag is None: dhcp_tag = "" self.dhcp_tag = dhcp_tag def set_server(self, server): if server in [None, ""]: server = "<<inherit>>" self.server = server def set_next_server(self, server): if server in [None, ""]: self.next_server = "<<inherit>>" else: server = server.strip() if server != "<<inherit>>": self.next_server = validate.ipv4_address(server) else: self.next_server = server def set_autoinstall(self, autoinstall): """ Set the automatic OS installation template file path, this must be a local file. @param str local automatic installation template path """ autoinstall_mgr = autoinstall_manager.AutoInstallationManager( self.collection_mgr) self.autoinstall = autoinstall_mgr.validate_autoinstall_template_file_path( autoinstall) def set_virt_auto_boot(self, num): utils.set_virt_auto_boot(self, num) def set_virt_cpus(self, num): utils.set_virt_cpus(self, num) def set_virt_file_size(self, num): utils.set_virt_file_size(self, num) def set_virt_disk_driver(self, driver): utils.set_virt_disk_driver(self, driver) def set_virt_ram(self, num): utils.set_virt_ram(self, num) def set_virt_type(self, vtype): utils.set_virt_type(self, vtype) def set_virt_bridge(self, vbridge): utils.set_virt_bridge(self, vbridge) def set_virt_path(self, path): utils.set_virt_path(self, path) def set_repos(self, repos, bypass_check=False): utils.set_repos(self, repos, bypass_check) def set_redhat_management_key(self, management_key): if not management_key: self.redhat_management_key = "<<inherit>>" self.redhat_management_key = management_key def get_redhat_management_key(self): return self.redhat_management_key