diff options
-rw-r--r-- | CHANGELOG | 3 | ||||
-rw-r--r-- | cobbler/action_litesync.py | 2 | ||||
-rw-r--r-- | cobbler/action_sync.py | 7 | ||||
-rwxr-xr-x | cobbler/cobbler.py | 25 | ||||
-rw-r--r-- | cobbler/item_system.py | 222 | ||||
-rw-r--r-- | cobbler/utils.py | 21 | ||||
-rw-r--r-- | docs/cobbler.pod | 18 |
7 files changed, 228 insertions, 70 deletions
@@ -2,7 +2,8 @@ Cobbler CHANGELOG (all entries mdehaan@redhat.com unless noted otherwise) * Mon Oct 08 2007 - 0.6.3 -- Replace @@server@@ in repo config files +- Be able to define and use Multiple NICs per system +- Use server address not @@server in repo config files * Fri Sep 28 2007 - 0.6.2 - cobbler repo auto-add to discover yum repos automatically diff --git a/cobbler/action_litesync.py b/cobbler/action_litesync.py index d86962f..61b4ee5 100644 --- a/cobbler/action_litesync.py +++ b/cobbler/action_litesync.py @@ -113,9 +113,11 @@ class BootLiteSync: self.sync.rmfile(os.path.join(self.settings.webdir, "systems", name)) # delete contents of kickstarts_sys/$name in webdir system_record = self.systems.find(name=name) + # FIXME: make this understand multiple interfaces filename = utils.get_config_filename(system_record) self.sync.rmtree(os.path.join(self.settings.webdir, "kickstarts_sys", filename)) + # FIXME: make this understand multiple interfaces if not system_record.is_pxe_supported(): # no need to go any further with PXE cleanup return diff --git a/cobbler/action_sync.py b/cobbler/action_sync.py index 9cd7b46..03716d4 100644 --- a/cobbler/action_sync.py +++ b/cobbler/action_sync.py @@ -460,7 +460,6 @@ class BootSync: meta = utils.blender(False, s) kickstart_path = utils.find_kickstart(meta["kickstart"]) if kickstart_path and os.path.exists(kickstart_path): - # pxe_fn = utils.get_config_filename(s) copy_path = os.path.join(self.settings.webdir, "kickstarts_sys", # system kickstarts go here s.name @@ -602,6 +601,8 @@ class BootSync: distro = profile.get_conceptual_parent() if distro is None: raise CX(_("profile %(profile)s references a missing distro %(distro)s") % { "profile" : system.profile, "distro" : profile.distro}) + + # FIXME: make this understand multiple interfaces: f1 = utils.get_config_filename(system) # tftp only @@ -615,12 +616,13 @@ class BootSync: if system.get_ip_address() == None: print _("Warning: Itanium system object (%s) needs an IP address to PXE") % system.name - + # FIXME: make this understand multiple interfaces filename = "%s.conf" % utils.get_config_filename(system) f2 = os.path.join(self.settings.tftpboot, filename) f3 = os.path.join(self.settings.webdir, "systems", f1) + # FIXME: make this understand multiple interfaces if system.netboot_enabled and system.is_pxe_supported(): if distro.arch in [ "x86", "x86_64", "standard"]: self.write_pxe_file(f2,system,profile,distro,False) @@ -720,7 +722,6 @@ class BootSync: if kickstart_path is not None and kickstart_path != "": if system is not None and kickstart_path.startswith("/"): - # pxe_fn = utils.get_config_filename(system) kickstart_path = "http://%s/cblr/kickstarts_sys/%s/ks.cfg" % (self.settings.server, system.name) elif kickstart_path.startswith("/") or kickstart_path.find("/cobbler/kickstarts/") != -1: kickstart_path = "http://%s/cblr/kickstarts/%s/ks.cfg" % (self.settings.server, profile.name) diff --git a/cobbler/cobbler.py b/cobbler/cobbler.py index 1fa6821..38c971c 100755 --- a/cobbler/cobbler.py +++ b/cobbler/cobbler.py @@ -494,6 +494,7 @@ class BootCLI: """ Create/Edit a system: 'cobbler system edit --name='foo' ... """ + # This copy/paste is heinous evil, please forgive me. commands = { '--name' : lambda(a) : sys.set_name(a), '--newname' : lambda(a) : True, @@ -502,18 +503,30 @@ class BootCLI: '--kopts' : lambda(a) : sys.set_kernel_options(a), '--ksmeta' : lambda(a) : sys.set_ksmeta(a), '--hostname' : lambda(a) : sys.set_hostname(a), - '--pxe-address' : lambda(a) : sys.set_ip_address(a), # deprecated - '--ip-address' : lambda(a) : sys.set_ip_address(a), - '--ip' : lambda(a) : sys.set_ip_address(a), # alias - '--mac-address' : lambda(a) : sys.set_mac_address(a), - '--mac' : lambda(a) : sys.set_mac_address(a), # alias + '--ip' : lambda(a) : sys.set_ip_address(a,interface=0), + '--mac' : lambda(a) : sys.set_mac_address(a,interface=0), + '--gateway' : lambda(a) : sys.set_mac_address(a,interface=0), + '--subnet' : lambda(a) : sys.set_mac_address(a,interface=0), + '--virt-bridge' : lambda(a) : sys.set_mac_address(a,interface=0), '--kickstart' : lambda(a) : sys.set_kickstart(a), - '--kick-start' : lambda(a) : sys.set_kickstart(a), '--netboot-enabled' : lambda(a) : sys.set_netboot_enabled(a), '--virt-path' : lambda(a) : sys.set_virt_path(a), '--virt-type' : lambda(a) : sys.set_virt_type(a), '--dhcp-tag' : lambda(a) : sys.set_dhcp_tag(a) } + + # add some command aliases for additional interfaces. The default commands + # only operate on the first, which are all many folks will need. + for count in range(0,7): + commands["--hostname%d" % count] = lambda(a) : sys.set_hostname(a,interface=count) + commands["--ip%d" % count] = lambda(a) : sys.set_ip_address(a,interface=count) + commands["--mac%d" % count] = lambda(a) : sys.set_mac_address(a,interface=0) + commands["--gateway%d" % count] = lambda(a) : sys.set_gateway(a,interface=0) + commands["--subnet%d" % count] = lambda(a) : sys.set_subnet(a,interface=0) + commands["--virt-bridge%d" % count] = lambda(a) : sys.set_virt_bridge(a,interface=0) + commands["--dhcp-tag%d" % count] = lambda(a) : sys.set_dhcp_tag(a,interface=0) + + def on_ok(): self.api.systems().add(sys, with_copy=True) return self.apply_args(args,commands,on_ok) diff --git a/cobbler/item_system.py b/cobbler/item_system.py index 03a8f69..1a130f3 100644 --- a/cobbler/item_system.py +++ b/cobbler/item_system.py @@ -34,12 +34,14 @@ class System(item.Item): self.profile = (None, '<<inherit>>')[is_subobject] self.kernel_options = ({}, '<<inherit>>')[is_subobject] self.ks_meta = ({}, '<<inherit>>')[is_subobject] - self.ip_address = ("", '<<inherit>>')[is_subobject] - self.mac_address = ("", '<<inherit>>')[is_subobject] + + # these are obsolete: + # self.ip_address = ("", '<<inherit>>')[is_subobject] + # self.mac_address = ("", '<<inherit>>')[is_subobject] + # self.hostname = ("", '<<inheirt>>')[is_subobject] + self.netboot_enabled = (1, '<<inherit>>')[is_subobject] - self.hostname = ("", '<<inheirt>>')[is_subobject] self.depth = 2 - self.dhcp_tag = "default" self.kickstart = "<<inherit>>" # use value in profile self.virt_path = "<<inherit>>" # use value in profile self.virt_type = "<<inherit>>" # use value in profile @@ -55,20 +57,68 @@ class System(item.Item): self.kickstart = self.load_item(seed_data, 'kickstart', '<<inherit>>') self.virt_path = self.load_item(seed_data, 'virt_path', '<<inherit>>') self.virt_type = self.load_item(seed_data, 'virt_type', '<<inherit>>') - self.dhcp_tag = self.load_item(seed_data, 'dhcp_tag', 'default') + + # backwards compat, this is now a per-NIC setting + __dhcp_tag = self.load_item(seed_data, 'dhcp_tag', 'default') # backwards compat, load --ip-address from two possible sources. # the old --pxe-address was a bit of a misnomer, new value is --ip-address - - oldvar = self.load_item(seed_data, 'pxe_address') - if oldvar == "": # newer version, yay - self.ip_address = self.load_item(seed_data, 'ip_address') + # though the ultimate trick is that we don't even use this parameter anymore for + # storage reasons. We now use self.interfaces which is an array of hashes. + __oldvar = self.load_item(seed_data, 'pxe_address', "") + if __oldvar == "": # newer version, yay + __ip_address = self.load_item(seed_data, 'ip_address', "") else: - self.ip_address = oldvar - + __ip_address = __oldvar + + # the following two parameters are also here for backwards compatibility reasons + __hostname = self.load_item(seed_data, 'hostname', "") + __mac_address = self.load_item(seed_data, 'mac_address', "") + + # now load the new-style interface definition data structure + self.interfaces = self.load_item(seed_data, 'interfaces', []) + + # now backfill the interface structure with any old values + # backwards compatibility here is complex/ugly though we don't want to + # break anyone. So this code needs to stay here. + if __hostname is not None or + __mac_address is not None or + __ip_address is not None: + insert_item = {} + if __hostname is not None and __hostname != "": + insert_item["hostname"] = __hostname + if __mac_address is not None and __mac_address != "": + insert_item["mac_address"] = __mac_address + if __ip_address is not None and __ip_address != "": + insert_item["ip_address"] = __ip_address + if __dhcp_tag is not None and __dhcp_tag != "": + insert_item["dhcp_tag"] = __dhcp_tag + self.interfaces.append(insert_item) + + # now if no interfaces are STILL defined, add in one only under certain + # conditions .. this emulates legacy behavior in the new interface format + if __mac_address == "" and utils.is_mac(self.name): + self.interfaces.append({ + "mac_address" : self.name + }) + elif __ip_address == "" and utils.is_ip(self.name): + self.interfaces.append({ + "ip_address" : self.ip_address + }) + + # now for each interface, if any fields are missing, add them. + for x in self.interfaces: + x.setdefault("mac_address","") + x.setdefault("ip_address","") + x.setdefault("netmask","") + x.setdefault("hostname","") + x.setdefault("gateway","") + x.setdefault("virt_bridge","") + x.setdefault("dhcp_tag","default") + + # question: is there any case where we'd want to PXE-enable one interface + # but not another. answer: probably not. self.netboot_enabled = self.load_item(seed_data, 'netboot_enabled', 1) - self.hostname = self.load_item(seed_data, 'hostname') - self.mac_address = self.load_item(seed_data, 'mac_address') # backwards compatibility -- convert string entries to dicts for storage # this allows for better usage from the API. @@ -78,16 +128,7 @@ class System(item.Item): if self.ks_meta != "<<inherit>>" and type(self.ks_meta) != dict: self.set_ksmeta(self.ks_meta) - # backwards compatibility -- if name is an IP or a MAC, set appropriate fields - # this will only happen once, as part of an upgrade path ... - # Explanation -- older cobbler's figured out the MAC and IP - # from the system name, newer cobblers allow arbitrary naming but can tell when the - # name is an IP or a MAC and use that if that info is not supplied. - if self.mac_address == "" and utils.is_mac(self.name): - self.mac_address = self.name - elif self.ip_address == "" and utils.is_ip(self.name): - self.ip_address = self.name return self @@ -117,33 +158,41 @@ class System(item.Item): return True - def get_mac_address(self): + def get_mac_address(self,interface=0): """ Get the mac address, which may be implicit in the object name or explicit with --mac-address. Use the explicit location first. """ - if self.mac_address != "": - return self.mac_address - elif utils.is_mac(self.name): + + if len(self.interfaces) -1 < interface: + raise CX(_("internal error: probing an interface that does not exist")) + + intf = self.interfaces[interface] + if intf["mac_address"] != "": + return intf["mac_address"] + elif utils.is_mac(self.name) and interface == 0: return self.name else: - # no one ever set it, but that might be ok depending on usage. return None - def get_ip_address(self): + def get_ip_address(self,interface=0): """ Get the IP address, which may be implicit in the object name or explict with --ip-address. Use the explicit location first. """ - if self.ip_address != "": - return self.ip_address - elif utils.is_ip(self.name): + + if len(self.interfaces) -1 < interface: + raise CX(_("internal error: probing an interface that does not exist")) + + intf = self.interfaces[interface] + if intf["ip_address"] != "": + return intf["ip_address"] + elif utils.is_ip(self.name) and interface == 0: return self.name else: - # no one ever set it, but that might be ok depending on usage. return None - def is_pxe_supported(self): + def is_pxe_supported(self,interface=0): """ Can only add system PXE records if a MAC or IP address is available, else it's a koan only record. Actually Itanium goes beyond all this and needs the IP all of the time @@ -151,39 +200,83 @@ class System(item.Item): """ if self.name == "default": return True - mac = self.get_mac_address() - ip = self.get_ip_address() + counter = 0 + mac = self.get_mac_address(counter) + ip = self.get_ip_address(counter) if mac is None and ip is None: return False return True - def set_dhcp_tag(self,dhcp_tag): - self.dhcp_tag = dhcp_tag + def __get_interface(self,interface=0,enforcing=False): + if not ( interface <= len(self.interfaces) -1 ): + if enforcing: + raise CX(_("internal error: accessing interface that does not exist: %s" % interface)) + else: + # if there are two interfaces and the API requests the fifth, that's an error. + # you can't set an interface that is more than +1 away from the current count. + # FIXME: this may cause problems. + # FIXME: there should be a function that generates this. + new_item = { + "hostname" : "", + "ip_address" : "", + "mac_address" : "", + "subnet" : "", + "netmask" : "", + "virt_bridge" : "", + "dhcp_tag" : "" + } + self.interfaces.append(new_item) + return new_item + + return self.interfaces[interface] + + def set_dhcp_tag(self,dhcp_tag,interface=0): + intf = self.__get_interface(interface) + intf["dhcp_tag"] = dhcp_tag return True - def set_hostname(self,hostname): - self.hostname = hostname + def set_hostname(self,hostname,interface=0): + intf = self.__get_interface(interface) + intf["hostname"] = hostname return True - def set_ip_address(self,address): + def set_ip_address(self,address,interface=0): """ Assign a IP or hostname in DHCP when this MAC boots. Only works if manage_dhcp is set in /var/lib/cobbler/settings """ + # FIXME: interface + intf = self.__get_interface(interface) if utils.is_ip(address): - self.ip_address = address + intf["ip_address"] = address return True raise CX(_("invalid format for IP address")) - def set_mac_address(self,address): + def set_mac_address(self,address,interface=0): + # FIXME: interface + intf = self.__get_interface(interface) if utils.is_mac(address): - self.mac_address = address + intf["mac_address"] = address return True raise CX(_("invalid format for MAC address")) - def set_pxe_address(self,address): - # backwards compatibility for API users: - return self.set_ip_address(address) + def set_gateway(self,gateway,interface=0): + # FIXME: validate + interface + intf = self.__get_interface(interface) + intf["gateway"] = gateway + return True + + def set_subnet(self,subnet,interface=0): + # FIXME: validate + interface + intf = self.__get_interface(interface) + intf["subnet"] = subnet + return True + + def set_virt_bridge(self,bridge,interface=0): + # FIXME: validate + interface + intf = self.__get_interface(interface) + intf["bridge"] = bridge + return True def set_profile(self,profile_name): """ @@ -272,16 +365,17 @@ class System(item.Item): 'profile' : self.profile, 'kernel_options' : self.kernel_options, 'ks_meta' : self.ks_meta, - 'ip_address' : self.ip_address, + #'ip_address' : self.ip_address, 'netboot_enabled' : self.netboot_enabled, - 'hostname' : self.hostname, - 'mac_address' : self.mac_address, + #'hostname' : self.hostname, + #'mac_address' : self.mac_address, 'parent' : self.parent, 'depth' : self.depth, 'kickstart' : self.kickstart, 'virt_type' : self.virt_type, 'virt_path' : self.virt_path, - 'dhcp_tag' : self.dhcp_tag + #'dhcp_tag' : self.dhcp_tag, + 'interfaces' : self.interfaces } def printable(self): @@ -289,16 +383,27 @@ class System(item.Item): buf = buf + _("profile : %s\n") % self.profile buf = buf + _("kernel options : %s\n") % self.kernel_options buf = buf + _("ks metadata : %s\n") % self.ks_meta - buf = buf + _("ip address : %s\n") % self.get_ip_address() - buf = buf + _("mac address : %s\n") % self.get_mac_address() - buf = buf + _("hostname : %s\n") % self.hostname - buf = buf + _("pxe info set? : %s\n") % self.is_pxe_supported() - buf = buf + _("config id : %s\n") % utils.get_config_filename(self) + buf = buf + _("netboot enabled? : %s\n") % self.netboot_enabled buf = buf + _("kickstart : %s\n") % self.kickstart buf = buf + _("virt type : %s\n") % self.virt_type buf = buf + _("virt path : %s\n") % self.virt_path - buf = buf + _("dhcp tag : %s\n") % self.dhcp_tag + + counter = 0 + for x in self.interfaces: + buf = buf + _(" -----------\n") + buf = buf + _("interface : #%d\n") % counter + 1 + buf = buf + _(" mac address : %s\n") % x.get("mac_address","") + buf = buf + _(" ip address : %s\n") % x.get("ip_address","") + buf = buf + _(" hostname : %s\n") % x.get("hostname","") + buf = buf + _(" gateway : %s\n") % x.get("gateway","") + buf = buf + _(" subnet : %s\n") % x.get("subnet","") + buf = buf + _(" virt bridge : %s\n") % x.get("virt_bridge","") + buf = buf + _(" dhcp tag : %s\n") % x.get("dhcp_tag","") + buf = buf + _(" config id : %s\n") % utils.get_config_filename(self,counter) + counter = counter + 1 + + return buf def remote_methods(self): @@ -316,6 +421,9 @@ class System(item.Item): 'netboot-enabled' : self.set_netboot_enabled, 'virt-path' : self.set_virt_path, 'virt-type' : self.set_virt_type, - 'dhcp-tag' : self.set_dhcp_tag + 'dhcp-tag' : self.set_dhcp_tag, + 'gateway' : self.set_gateway, + 'virt-bridge' : self.set_virt_bridge, + 'subnet' : self.set_subnet } diff --git a/cobbler/utils.py b/cobbler/utils.py index 2feee70..a2cefbb 100644 --- a/cobbler/utils.py +++ b/cobbler/utils.py @@ -55,7 +55,7 @@ def get_host_ip(ip): results = out.read() return results.split(" ")[-1][0:8] -def get_config_filename(sys): +def get_config_filename(sys,interface=0): """ 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 @@ -64,10 +64,14 @@ def get_config_filename(sys): system.is_pxe_supported()). This same file is used to store system config information in the Apache tree, so it's still relevant. """ + + if interface > len(sys.interfaces) -1: + raise CX(_("internal error: probing an interface that does not exist")) + if sys.name == "default": return "default" - mac = sys.get_mac_address() - ip = sys.get_ip_address() + mac = sys.get_mac_address(nterface) + ip = sys.get_ip_address(interface) if mac != None: return "01-" + "-".join(mac.split(":")).lower() elif ip != None: @@ -308,6 +312,17 @@ def flatten(data): data["repos"] = " ".join(data["repos"]) if data.has_key("rpm_list") and type(data["rpm_list"]) == list: data["rpm_list"] = " ".join(data["rpm_list"]) + if data.has_key("interfaces"): + # make interfaces accessible without Cheetah-voodoo in the templates + # EXAMPLE: $ip == $ip0, $ip1, $ip2 and so on. + counter = 0 + for x in data["interfaces"]: + data["%s%d" % (x,counter)] = data["interfaces"][x] + # just to keep templates backwards compatibile + if counter == 0: + data[x] = data["interfaces"][x] + counter = counter + 1 + return data def __consolidate(node,results): diff --git a/docs/cobbler.pod b/docs/cobbler.pod index 8326987..0962278 100644 --- a/docs/cobbler.pod +++ b/docs/cobbler.pod @@ -204,6 +204,8 @@ already looks like a mac address, this is inferred from the system name and does MAC addresses have the format AA:BB:CC:DD:EE:FF. +If you would like to specify additional MACs, use --mac0=x, --mac1=y and so on. + =item ip If cobbler is configured to generate a DHCP configuratition (see advanced section), use this @@ -214,6 +216,8 @@ Example: ---ip=192.168.1.50 Note for Itanium users: this setting is always required for IA64 regardless of whether DHCP management is enabled. +If you would like to specify additional IPs, use --ip0=x, --ip1=y and so on. + =item hostname If using the DHCP configuration feature (see advanced section) with dnsmasq, use this to define a hostname for the system to @@ -222,6 +226,18 @@ basically ignored. Example: --hostname=mycomputer.example.com +If you would like to specify additional hostnames, use --hostname0=x, --hostname1=y and so on. + +=item --gateway and --subnet + +If you are using static IP configurations, you may find it useful to store gateway and subnet +information inside of cobbler. These variables are not used internally by cobbler, but are +made available in cobbler templates, which are described later in this document and on the Wiki. +For DHCP configurations, these parameters should be left blank. + +To describe gateway and subnet information for multiple intefaces, use --gateway0=x, --gateway1=y +and so on. Subnets work the same way. + =item --kickstart (optional) While it is recommended that the --kickstart parameter is only used within for the "profile add" command, there are scenarios when an install base switching to cobbler may have kickstarts created on a per-system basis (one kickstart for each system, nothing shared) and may not want to immediately make use of the cobbler templating system. This allows specifing a kickstart for use on a per-system basis. Creation of a parent profile is still required. If the kickstart is a filesystem location, it will still be treated as a cobbler template. @@ -237,6 +253,8 @@ If you are setting up a PXE environment with multiple subnets/gateways, and are By default, the dhcp tag for all systems is "default" and means that in the DHCP template files the systems will expand out where $insert_cobbler_systems_definitions is found in the DHCP template. However, you may want certain systems to expand out in other places in the file. Setting --dhcp-tag=subnet2 for instance, will cause that system to expand out where $insert_cobbler_system_definitions_subnet2 is found, allowing you to insert directives to specify different subnets (or other parameters) before the DHCP configuration entries for those particular systems. +If your system has multiple network interfaces, use --dhcp-tag0=x, --dhcp-tag1=y and so on. + =end =head2 ADDING A REPOSITORY TO MIRROR |