From 2cfbb55145b68cc9c91f2511d4f766a1cb96b1fd Mon Sep 17 00:00:00 2001 From: John Eckersberg Date: Sun, 11 Jan 2009 15:13:16 -0500 Subject: Whole bunch of network work, good time to throw a commit out. At this point I can manipulate network objects via the CLI. Nothing intelligent is going on under the hood yet. --- Makefile | 2 + cobbler.spec | 2 + cobbler/action_litesync.py | 6 ++ cobbler/action_report.py | 13 +++++ cobbler/api.py | 10 ++++ cobbler/collection.py | 3 + cobbler/collection_networks.py | 71 +++++++++++++++++++++++ cobbler/config.py | 18 ++++++ cobbler/item_network.py | 25 ++++---- cobbler/modules/cli_network.py | 129 +++++++++++++++++++++++++++++++++++++++++ cobbler/utils.py | 23 ++++++++ setup.py | 1 + 12 files changed, 291 insertions(+), 12 deletions(-) create mode 100644 cobbler/collection_networks.py create mode 100644 cobbler/modules/cli_network.py diff --git a/Makefile b/Makefile index 79b7d091..af9f6cd8 100644 --- a/Makefile +++ b/Makefile @@ -112,11 +112,13 @@ eraseconfig: -rm /var/lib/cobbler/profiles* -rm /var/lib/cobbler/systems* -rm /var/lib/cobbler/repos* + -rm /var/lib/cobbler/networks* -rm /var/lib/cobbler/config/distros.d/* -rm /var/lib/cobbler/config/images.d/* -rm /var/lib/cobbler/config/profiles.d/* -rm /var/lib/cobbler/config/systems.d/* -rm /var/lib/cobbler/config/repos.d/* + -rm /var/lib/cobbler/config/networks.d/* graphviz: diff --git a/cobbler.spec b/cobbler.spec index cb2f0cbe..7e632b82 100644 --- a/cobbler.spec +++ b/cobbler.spec @@ -88,6 +88,7 @@ if [ -e /var/lib/cobbler/distros ]; then cp /var/lib/cobbler/profiles* /var/lib/cobbler/backup 2>/dev/null cp /var/lib/cobbler/systems* /var/lib/cobbler/backup 2>/dev/null cp /var/lib/cobbler/repos* /var/lib/cobbler/backup 2>/dev/null + cp /var/lib/cobbler/networks* /var/lib/cobbler/backup 2>/dev/null fi if [ -e /var/lib/cobbler/config ]; then cp -a /var/lib/cobbler/config /var/lib/cobbler/backup 2>/dev/null @@ -217,6 +218,7 @@ test "x$RPM_BUILD_ROOT" != "x" && rm -rf $RPM_BUILD_ROOT %dir /var/lib/cobbler/config/systems.d/ %dir /var/lib/cobbler/config/repos.d/ %dir /var/lib/cobbler/config/images.d/ +%dir /var/lib/cobbler/config/networks.d/ %dir /var/lib/cobbler/kickstarts/ %dir /var/lib/cobbler/backup/ %dir /var/lib/cobbler/triggers diff --git a/cobbler/action_litesync.py b/cobbler/action_litesync.py index 7773455e..72172469 100644 --- a/cobbler/action_litesync.py +++ b/cobbler/action_litesync.py @@ -58,6 +58,7 @@ class BootLiteSync: self.images = config.images() self.settings = config.settings() self.repos = config.repos() + self.networks = config.networks() self.sync = config.api.get_sync(verbose) def add_single_distro(self, name): @@ -184,3 +185,8 @@ class BootLiteSync: else: utils.rmfile(os.path.join(bootloc, filename)) + # not sure sure I actually need anything special to litesync networks + def add_single_network(self, name): + pass + def remote_single_network(self, name): + pass diff --git a/cobbler/action_report.py b/cobbler/action_report.py index 2288433e..c12838e5 100644 --- a/cobbler/action_report.py +++ b/cobbler/action_report.py @@ -321,6 +321,12 @@ class Report: else: self.reporting_print_sorted(self.api.repos()) + if report_what in [ "all", "networks", "network" ]: + if report_name is not None: + self.reporting_list_names2(self.api.networks(), report_name) + else: + self.reporting_print_sorted(self.api.networks()) + if report_what in [ "all", "images", "image" ]: if report_name is not None: self.reporting_list_names2(self.api.images(), report_name) @@ -344,6 +350,9 @@ class Report: if report_what in [ "all", "repos", "repo" ]: self.reporting_print_all_fields(self.api.repos(), report_type, report_noheaders) + if report_what in [ "all", "networks", "network" ]: + self.reporting_print_all_fields(self.api.networks(), report_type, report_noheaders) + if report_what in [ "all", "images", "image" ]: self.reporting_print_all_fields(self.api.images(), report_type, report_noheaders) @@ -360,6 +369,10 @@ class Report: if report_what in [ "all", "repos", "repo" ]: self.reporting_print_x_fields(self.api.repos(), report_type, report_fields, report_noheaders) + + if report_what in [ "all", "networks", "network" ]: + self.reporting_print_x_fields(self.api.networks(), report_type, report_fields, report_noheaders) + if report_what in [ "all", "images", "image" ]: self.reporting_print_x_fields(self.api.images(), report_type, report_fields, report_noheaders) diff --git a/cobbler/api.py b/cobbler/api.py index c36d442a..315802e3 100644 --- a/cobbler/api.py +++ b/cobbler/api.py @@ -260,6 +260,12 @@ class BootAPI: """ return self._config.images() + def networks(self): + """ + Return the current list of networks + """ + return self._config.networks() + def settings(self): """ Return the application configuration @@ -373,6 +379,10 @@ class BootAPI: self.log("new_image",[is_subobject]) return self._config.new_image(is_subobject=is_subobject) + def new_network(self,is_subobject=False): + self.log("new_network",[is_subobject]) + return self._config.new_network(is_subobject=is_subobject) + def add_distro(self, ref, check_for_duplicate_names=False, save=True): self.log("add_distro",[ref.name]) rc = self._config.distros().add(ref,check_for_duplicate_names=check_for_duplicate_names,save=save) diff --git a/cobbler/collection.py b/cobbler/collection.py index 36c58b06..dead9fff 100644 --- a/cobbler/collection.py +++ b/cobbler/collection.py @@ -35,6 +35,7 @@ import item_profile import item_distro import item_repo import item_image +import item_network from utils import _ class Collection(serializable.Serializable): @@ -275,6 +276,8 @@ class Collection(serializable.Serializable): 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_network.Network): + self.lite_sync.add_single_network(ref.name) elif isinstance(ref, item_repo.Repo): pass else: diff --git a/cobbler/collection_networks.py b/cobbler/collection_networks.py new file mode 100644 index 00000000..3f778bbb --- /dev/null +++ b/cobbler/collection_networks.py @@ -0,0 +1,71 @@ +""" +Networks in cobbler allow network-level attributes to be defined and +to be inherited by systems/interfaces which belong to the network. +Also allows for intelligent allocation of addresses within networks. + +Copyright 2009, Red Hat, Inc +John Eckersberg + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA +""" + +import item_network as network +import utils +import collection +from cexceptions import * +from utils import _ +import os.path + +TESTMODE = False + +#-------------------------------------------- + +class Networks(collection.Collection): + + def collection_type(self): + return "network" + + def factory_produce(self,config,seed_data): + """ + Return a repo forged from seed_data + """ + return network.Network(config).from_datastruct(seed_data) + + def remove(self,name,with_delete=True,with_sync=True,with_triggers=True,recursive=False): + """ + Remove element named 'name' from the collection + """ + + # NOTE: with_delete isn't currently meaningful for networks + # 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: + self._run_triggers(obj, "/var/lib/cobbler/triggers/delete/network/pre/*") + + del self.listing[name] + self.config.serialize_delete(self, obj) + + if with_delete: + self.log_func("deleted network %s" % name) + if with_triggers: + self._run_triggers(obj, "/var/lib/cobbler/triggers/delete/network/post/*") + + return True + raise CX(_("cannot delete an object that does not exist: %s") % name) + diff --git a/cobbler/config.py b/cobbler/config.py index 2608b84d..a89ef380 100644 --- a/cobbler/config.py +++ b/cobbler/config.py @@ -32,12 +32,14 @@ import item_profile as profile import item_system as system import item_repo as repo import item_image as image +import item_network as network import collection_distros as distros import collection_profiles as profiles import collection_systems as systems import collection_repos as repos import collection_images as images +import collection_networks as networks import modules.serializer_yaml as serializer_yaml import settings @@ -76,6 +78,7 @@ class Config: self._profiles = profiles.Profiles(weakref.proxy(self)) self._systems = systems.Systems(weakref.proxy(self)) self._images = images.Images(weakref.proxy(self)) + self._networks = networks.Networks(weakref.proxy(self)) self._settings = settings.Settings() # not a true collection def generate_uid(self): @@ -133,6 +136,12 @@ class Config: """ return self._images + def networks(self): + """ + Return the definitive copy of the Networks collection + """ + return self._networks + def new_distro(self,is_subobject=False): """ Create a new distro object with a backreference to this object @@ -163,6 +172,12 @@ class Config: """ return image.Image(weakref.proxy(self),is_subobject=is_subobject) + def new_network(self,is_subobject=False): + """ + Create a new network object... + """ + return network.Network(weakref.proxy(self),is_subobject=is_subobject) + def clear(self): """ Forget about all loaded configuration data @@ -173,6 +188,7 @@ class Config: self._profiles.clear(), self._images.clear() self._systems.clear(), + self._networks.clear(), return True def serialize(self): @@ -184,6 +200,7 @@ class Config: serializer.serialize(self._profiles) serializer.serialize(self._images) serializer.serialize(self._systems) + serializer.serialize(self._networks) return True def serialize_item(self,collection,item): @@ -213,6 +230,7 @@ class Config: serializer.deserialize(self._profiles) serializer.deserialize(self._images) serializer.deserialize(self._systems) + serializer.deserialize(self._networks) return True def deserialize_raw(self,collection_type): diff --git a/cobbler/item_network.py b/cobbler/item_network.py index dbcd7ad4..a367abd5 100644 --- a/cobbler/item_network.py +++ b/cobbler/item_network.py @@ -23,8 +23,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA import utils import item from cexceptions import * -from utils import _ -import netaddr +from utils import _, _IP, _CIDR class Network(item.Item): @@ -64,28 +63,30 @@ class Network(item.Item): return self def set_cidr(self, cidr): - pass + self.cidr = _CIDR(cidr) def set_address(self, address): - pass + self.address = _IP(address) def set_gateway(self, gateway): - pass + self.gateway = _IP(gateway) def set_broadcast(self, broadcast): - pass + self.broadcast = _IP(broadcast) def set_nameservers(self, nameservers): - pass + nameservers = [s.strip() for s in nameservers.split(',')] + self.nameservers = [_IP(i) for i in nameservers] def set_reserved(self, reserved): - pass + reserved = [s.strip() for s in reserved.split(',')] + self.reserved = [_CIDR(c) for c in reserved] - def set_used_addresses(self, used_addresses): - pass +# def set_used_addresses(self, used_addresses): +# pass - def set_free_addresses(self, free_addresses): - pass +# def set_free_addresses(self, free_addresses): +# pass def is_valid(self): """ diff --git a/cobbler/modules/cli_network.py b/cobbler/modules/cli_network.py new file mode 100644 index 00000000..c20a77fc --- /dev/null +++ b/cobbler/modules/cli_network.py @@ -0,0 +1,129 @@ +""" +Network CLI module. + +Copyright 2009, Red Hat, Inc +John Eckersberg + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA +""" + +import distutils.sysconfig +import sys + +plib = distutils.sysconfig.get_python_lib() +mod_path="%s/cobbler" % plib +sys.path.insert(0, mod_path) + +from utils import _, _IP, _CIDR +import commands +import cexceptions + + +class NetworkFunction(commands.CobblerFunction): + + def help_me(self): + return commands.HELP_FORMAT % ("cobbler network"," [ARGS]") + + def command_name(self): + return "network" + + def subcommands(self): + return [ "add", "copy", "dumpvars", "edit", "find", "list", "remove", "rename", "report" ] + + def add_options(self, p, args): + if not self.matches_args(args,["dumpvars","remove","report","list"]): + p.add_option("--cidr", dest="cidr", help="CIDR representation of the network (REQUIRED)") + p.add_option("--address", dest="address", help="Network address") + p.add_option("--broadcast", dest="broadcast", help="Broadcast address") + p.add_option("--gateway", dest="gateway", help="Gateway address") + p.add_option("--ns", dest="ns", help="comma-delimited list of nameservers") + p.add_option("--reserved", dest="reserved", help="comma-delimited list of IP/CIDR to reserve") + p.add_option("--comment", dest="comment", help="user field") + + p.add_option("--name", dest="name", help="ex: 'vlan001' (REQUIRED)") + + if self.matches_args(args,["add"]): + p.add_option("--clobber", dest="clobber", help="allow add to overwrite existing objects", action="store_true") + if self.matches_args(args,["copy","rename"]): + p.add_option("--newname", dest="newname", help="used for copy/edit") + if not self.matches_args(args,["dumpvars","find","remove","report","list"]): + p.add_option("--no-sync", action="store_true", dest="nosync", help="suppress sync for speed") + if not self.matches_args(args,["dumpvars","find","report","list"]): + p.add_option("--no-triggers", action="store_true", dest="notriggers", help="suppress trigger execution") + if not self.matches_args(args,["dumpvars","remove","report","list"]): + p.add_option("--owners", dest="owners", help="specify owners for authz_ownership module") + + + def run(self): + if self.args and "find" in self.args: + items = self.api.find_network(return_list=True, no_errors=True, **self.options.__dict__) + for x in items: + print x.name + return True + + obj = self.object_manipulator_start(self.api.new_network,self.api.networks) + if obj is None: + return True + if self.matches_args(self.args,["dumpvars"]): + return self.object_manipulator_finish(obj, self.api.profiles, self.options) + + if self.options.cidr is not None: + obj.set_cidr(self.options.cidr) + + if self.options.address is not None: + obj.set_address(self.options.address) + elif self.matches_args(self.args, ["add"]): + obj.set_address(_CIDR(self.options.cidr)[0]) + + if self.options.broadcast is not None: + obj.set_broadcast(self.options.broadcast) + elif self.matches_args(self.args, ["add"]): + obj.set_broadcast(_CIDR(self.options.cidr)[-1]) + + if self.options.gateway is not None: + obj.set_gateway(self.options.gateway) + elif self.matches_args(self.args, ["add"]): + obj.set_gateway(_CIDR(self.options.cidr)[-2]) + + if self.options.ns is not None: + obj.set_nameservers(self.options.ns) + if self.options.reserved is not None: + obj.set_reserved(self.options.reserved) + if self.options.owners is not None: + obj.set_owners(self.options.owners) + if self.options.comment is not None: + obj.set_comment(self.options.comment) + + return self.object_manipulator_finish(obj, self.api.networks, self.options) + + + +######################################################## +# MODULE HOOKS + +def register(): + """ + The mandatory cobbler module registration hook. + """ + return "cli" + +def cli_functions(api): + return [ + NetworkFunction(api) + ] + return [] + + diff --git a/cobbler/utils.py b/cobbler/utils.py index 18535638..ca08e467 100644 --- a/cobbler/utils.py +++ b/cobbler/utils.py @@ -163,6 +163,29 @@ def get_host_ip(ip, shorten=True): cutoff = (32 - cidr.prefixlen) / 4 return pretty[0:-cutoff] +def _IP(ip): + """ + Returns a netaddr.IP object representing ip. + If ip is already an netaddr.IP instance just return it. + Else return a new instance + """ + if isinstance(ip, netaddr.IP): + return ip + else: + return netaddr.IP(ip) + +def _CIDR(cidr): + """ + Returns a netaddr.CIDR object representing cidr. + If cidr is already an netaddr.CIDR instance just return it. + Else return a new instance + """ + if isinstance(cidr, netaddr.CIDR): + return cidr + else: + return netaddr.CIDR(cidr) + + def get_config_filename(sys,interface): """ The configuration file for each system pxe uses is either diff --git a/setup.py b/setup.py index 62675a5e..082af307 100644 --- a/setup.py +++ b/setup.py @@ -177,6 +177,7 @@ if __name__ == "__main__": (dbpath + "/systems.d", []), (dbpath + "/repos.d", []), (dbpath + "/images.d", []), + (dbpath + "/networks.d", []), # sample kickstart files (kickpath, ['kickstarts/legacy.ks']), -- cgit