summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG3
-rw-r--r--cobbler/action_litesync.py2
-rw-r--r--cobbler/action_sync.py7
-rwxr-xr-xcobbler/cobbler.py25
-rw-r--r--cobbler/item_system.py222
-rw-r--r--cobbler/utils.py21
-rw-r--r--docs/cobbler.pod18
7 files changed, 228 insertions, 70 deletions
diff --git a/CHANGELOG b/CHANGELOG
index 93b2219..39a8ec7 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -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