Example #1
0
    def get_items(
        self, collection_type: str
    ) -> Union[Distros, Profiles, Systems, Repos, Images, Mgmtclasses,
               Packages, Files, Menus]:
        """
        Get a full collection of a single type.

        Valid Values vor ``collection_type`` are: "distro", "profile", "repo", "image", "mgmtclass", "package", "file"
        and "settings".

        :param collection_type: The type of collection to return.
        :return: The collection if ``collection_type`` is valid.
        :raises CX: If the ``collection_type`` is invalid.
        """
        result: Union[Distros, Profiles, Systems, Repos, Images, Mgmtclasses,
                      Packages, Files, Menus]
        if collection_type == "distro":
            result = self._distros
        elif collection_type == "profile":
            result = self._profiles
        elif collection_type == "system":
            result = self._systems
        elif collection_type == "repo":
            result = self._repos
        elif collection_type == "image":
            result = self._images
        elif collection_type == "mgmtclass":
            result = self._mgmtclasses
        elif collection_type == "package":
            result = self._packages
        elif collection_type == "file":
            result = self._files
        elif collection_type == "menu":
            result = self._menus
        elif collection_type == "settings":
            result = self.api.settings()
        else:
            raise CX("internal error, collection name \"%s\" not supported" %
                     collection_type)
        return result
Example #2
0
    def _get_power_input(self, system, power_operation, logger, user,
                         password):
        """
        Creates an option string for the fence agent from the system data. This is an internal method.

        :param system: Cobbler system
        :type system: System
        :param power_operation: power operation. Valid values: on, off, status. Rebooting is implemented as a set of 2
                                operations (off and on) in a higher level method.
        :type power_operation: str
        :param logger: logger
        :type logger: Logger
        :param user: user to override system.power_user
        :type user: str
        :param password: password to override system.power_pass
        :type password: str
        :return: The option string for the fencer agent.
        :rtype: str
        """

        self._check_power_conf(system, logger, user, password)
        power_input = ""
        if power_operation is None or power_operation not in [
                'on', 'off', 'status'
        ]:
            raise CX("invalid power operation")
        power_input += "action=" + power_operation + "\n"
        if system.power_address:
            power_input += "ip=" + system.power_address + "\n"
        if system.power_user:
            power_input += "username="******"\n"
        if system.power_id:
            power_input += "plug=" + system.power_id + "\n"
        if system.power_pass:
            power_input += "password="******"\n"
        if system.power_identity_file:
            power_input += "identity-file=" + system.power_identity_file + "\n"
        if system.power_options:
            power_input += system.power_options + "\n"
        return power_input
Example #3
0
    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/package/*", [],
                        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/package/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)
Example #4
0
    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:
            try:
                return self.listing.get(kargs["name"].lower(), None)
            except:
                return self.listing.get(kargs["name"], None)

        self.lock.acquire()
        try:
            for (name, obj) in list(self.listing.items()):
                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
Example #5
0
    def deserialize(self):
        """
        Load all cobbler_collections from disk

        :raises CX: if there is an error in deserialization
        """
        for collection in (
            self._menus,
            self._distros,
            self._repos,
            self._profiles,
            self._images,
            self._systems,
            self._mgmtclasses,
            self._packages,
            self._files,
        ):
            try:
                serializer.deserialize(collection)
            except Exception as e:
                raise CX("serializer: error loading collection %s: %s. Check /etc/cobbler/modules.conf"
                         % (collection.collection_type(), e)) from e
Example #6
0
    def set_boot_loaders(self, boot_loaders):
        """
        Setter of the boot loaders.

        :param boot_loaders: The boot loaders for the image.
        """
        # allow the magic inherit string to persist
        if boot_loaders == "<<inherit>>":
            self.boot_loaders = "<<inherit>>"
            return

        if boot_loaders:
            boot_loaders_split = utils.input_string_or_list(boot_loaders)
            supported_boot_loaders = self.get_supported_boot_loaders()

            if not set(boot_loaders_split).issubset(supported_boot_loaders):
                raise CX(
                    "Error with image %s - not all boot_loaders %s are supported %s"
                    % (self.name, boot_loaders_split, supported_boot_loaders))
            self.boot_loaders = boot_loaders_split
        else:
            self.boot_loaders = []
Example #7
0
def __parse_config():
    """
    Parse the "users.conf" of Cobbler and return all data in a dictionary.

    :return: The data seperated by sections. Each section has a subdictionary with the key-value pairs.
    :rtype: dict
    """
    etcfile = '/etc/cobbler/users.conf'
    if not os.path.exists(etcfile):
        raise CX(_("/etc/cobbler/users.conf does not exist"))
    # Make users case sensitive to handle kerberos
    config = ConfigParser()
    config.optionxform = str
    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
Example #8
0
    def set_boot_loaders(self, boot_loaders):
        """
        Set the bootloader for the distro.

        :param name: The name of the bootloader. Must be one of the supported ones.
        """
        # allow the magic inherit string to persist
        if boot_loaders == "<<inherit>>":
            self.boot_loaders = "<<inherit>>"
            return

        if boot_loaders:
            names_split = utils.input_string_or_list(boot_loaders)
            supported_distro_boot_loaders = self.get_supported_boot_loaders()

            if not set(names_split).issubset(supported_distro_boot_loaders):
                raise CX(
                    "Invalid boot loader names: %s. Supported boot loaders are: %s"
                    % (boot_loaders, ' '.join(supported_distro_boot_loaders)))
            self.boot_loaders = names_split
        else:
            self.boot_loaders = []
Example #9
0
    def remove(self,
               name,
               with_delete: bool = True,
               with_sync: bool = True,
               with_triggers: bool = True,
               recursive: bool = False):
        """
        Remove element named 'name' from the collection

        :raises CX: In case a non existent object should be deleted.
        """
        name = name.lower()
        obj = self.find(name=name)

        if obj is None:
            raise CX("cannot delete an object that does not exist: %s" % name)

        if with_delete:
            if with_triggers:
                utils.run_triggers(self.api, obj,
                                   "/var/lib/cobbler/triggers/delete/file/*",
                                   [])

        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.api, obj,
                    "/var/lib/cobbler/triggers/delete/file/post/*", [])
                utils.run_triggers(self.api, obj,
                                   "/var/lib/cobbler/triggers/change/*", [])

        return
Example #10
0
    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
Example #11
0
    def generate_autoinstall_for_profile(self, g):
        """
        Generate an autoinstall config or script for a profile.

        :param g: The Profile to generate the script/config for.
        :return: The generated output or an error message with a human readable description.
        :rtype: str
        """
        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)
Example #12
0
def serialize_item(collection, item):
    """
    Save a collection item to file system

    @param Collection collection collection
    @param Item item collection item
    """

    if item.name is None or item.name == "":
        raise CX("name unset for item!")

    # FIXME: Need a better way to support collections/items
    # appending an 's' does not work in all cases
    if collection.collection_type() in ['mgmtclass']:
        filename = "/var/lib/cobbler/collections/%ses/%s" % (
            collection.collection_type(), item.name)
    else:
        filename = "/var/lib/cobbler/collections/%ss/%s" % (
            collection.collection_type(), item.name)

    _dict = item.to_dict()

    if capi.CobblerAPI().settings().serializer_pretty_json:
        sort_keys = True
        indent = 4
    else:
        sort_keys = False
        indent = None

    filename += ".json"
    _dict = item.to_dict()
    fd = open(filename, "w+")
    data = simplejson.dumps(_dict,
                            encoding="utf-8",
                            sort_keys=sort_keys,
                            indent=indent)
    fd.write(data)

    fd.close()
Example #13
0
    def write_tftpd_files(self):
        """
        xinetd files are written when manage_tftp is set in
        /var/lib/cobbler/settings.
        """
        template_file = "/etc/cobbler/tftpd.template"

        try:
            f = open(template_file, "r")
        except:
            raise CX(_("error reading template %s") % template_file)
        template_data = ""
        template_data = f.read()
        f.close()

        metadata = {
            "user": "******",
            "binary": "/usr/sbin/in.tftpd",
            "args": "%s" % self.bootloc
        }
        self.logger.info("generating %s" % self.settings_file)
        self.templar.render(template_data, metadata, self.settings_file, None)
Example #14
0
    def generate_autoinstall_for_system(self, sys_name):
        """
        Generate an autoinstall config or script for a system.

        :param sys_name: The system name to generate an autoinstall script for.
        :return: The generated output or an error message with a human readable description.
        :rtype: str
        """
        s = self.api.find_system(name=sys_name)
        if s is None:
            return "# system not found"

        p = s.get_conceptual_parent()
        if p is None:
            raise CX("system %(system)s references missing profile %(profile)s" % {"system": s.name, "profile": s.profile})

        distro = p.get_conceptual_parent()
        if distro is None:
            # this is an image parented system, no automatic installation file available
            return "# image based systems do not have automatic installation files"

        return self.generate_autoinstall(profile=p, system=s)
Example #15
0
 def check_if_valid(self):
     """
     Insure name, path, owner, group, and mode are set.
     Templates are only required for files, is_dir = False
     """
     if not self.name:
         raise CX("name is required")
     if not self.path:
         raise CX("path is required")
     if not self.owner:
         raise CX("owner is required")
     if not self.group:
         raise CX("group is required")
     if not self.mode:
         raise CX("mode is required")
     if not self.is_dir and self.template == "":
         raise CX("Template is required when not a directory")
Example #16
0
def authenticate(api_handle, username, password):
    """
    Validate a username/password combo. This will pass the username and password back to Spacewalk to see if this
    authentication request is valid.

    See also: https://github.com/uyuni-project/uyuni/blob/master/java/code/src/com/redhat/rhn/frontend/xmlrpc/auth/AuthHandler.java#L133

    :param api_handle: The api instance to retrieve settings of.
    :param username: The username to authenticate agains spacewalk/uyuni/SUSE Manager
    :param password: The password to authenticate agains spacewalk/uyuni/SUSE Manager
    :return: True if it succeeded, False otherwise.
    :rtype: bool
    """

    if api_handle is None:
        raise CX("api_handle required. Please don't call this without it.")
    server = api_handle.settings().redhat_management_server
    user_enabled = api_handle.settings().redhat_management_permissive

    spacewalk_url = "https://%s/rpc/api" % server
    with ServerProxy(spacewalk_url, verbose=True) as client:
        if username == 'taskomatic_user' or __looks_like_a_token(password):
            # The tokens are lowercase hex, but a password can also be lowercase hex, so we have to try it as both a
            # token and then a password if we are unsure. We do it this way to be faster but also to avoid any login
            # failed stuff in the logs that we don't need to send.

            # Problem at this point, 0xdeadbeef is valid as a token but if that fails, it's also a valid password, so we
            # must try auth system #2

            if __check_auth_token(client, api_handle, username, password) != 1:
                return __check_user_login(client, api_handle, user_enabled,
                                          username, password)
            return True
        else:
            # It's an older version of spacewalk, so just try the username/pass.
            # OR: We know for sure it's not a token because it's not lowercase hex.
            return __check_user_login(client, api_handle, user_enabled,
                                      username, password)
Example #17
0
def get_module_name(category: str,
                    field: str,
                    fallback_module_name: Optional[str] = None) -> str:
    """
    Get module name from configuration file (currently hardcoded ``/etc/cobbler/modules.conf``).

    :param category: Field category in configuration file.
    :param field: Field in configuration file
    :param fallback_module_name: Default value used if category/field is not found in configuration file
    :raises FileNotFoundError: If unable to find configuration file.
    :raises ValueError: If the category does not exist or the field is empty.
    :raises CX: If the field could not be read and no fallback_module_name was given.
    :returns: The name of the module.
    """
    modules_conf_path = "/etc/cobbler/modules.conf"
    if not os.path.exists(modules_conf_path):
        raise FileNotFoundError("Configuration file at \"%s\" not found" %
                                modules_conf_path)

    cp = ConfigParser()
    cp.read(modules_conf_path)

    # FIXME: We can't enabled this check since it is to strict atm.
    # if category not in MODULES_BY_CATEGORY:
    #     raise ValueError("category must be one of: %s" % MODULES_BY_CATEGORY.keys())

    if field.isspace():
        raise ValueError(
            "field cannot be empty. Did you mean \"module\" maybe?")

    try:
        value = cp.get(category, field)
    except:
        if fallback_module_name is not None:
            value = fallback_module_name
        else:
            raise CX("Cannot find config file setting for: %s" % field)
    return value
Example #18
0
    def rsync_gen(self):
        """
        Generate rsync modules of all repositories and distributions
        """
        template_file = "/etc/cobbler/rsync.template"

        try:
            template = open(template_file, "r")
        except:
            raise CX("error reading template %s" % template_file)

        template_data = ""
        template_data = template.read()
        template.close()

        distros = []

        for link in glob.glob(os.path.join(self.settings.webdir, 'links',
                                           '*')):
            distro = {}
            distro["path"] = os.path.realpath(link)
            distro["name"] = os.path.basename(link)
            distros.append(distro)

        repos = [
            repo.name for repo in self.api.repos() if os.path.isdir(
                os.path.join(self.settings.webdir, "repo_mirror", repo.name))
        ]

        metadata = {
            "date": time.asctime(time.gmtime()),
            "cobbler_server": self.settings.server,
            "distros": distros,
            "repos": repos,
            "webdir": self.settings.webdir
        }

        self.templar.render(template_data, metadata, "/etc/rsyncd.conf")
Example #19
0
 def get_items(self, collection_type):
     if collection_type == "distro":
         result = self._distros
     elif collection_type == "profile":
         result = self._profiles
     elif collection_type == "system":
         result = self._systems
     elif collection_type == "repo":
         result = self._repos
     elif collection_type == "image":
         result = self._images
     elif collection_type == "mgmtclass":
         result = self._mgmtclasses
     elif collection_type == "package":
         result = self._packages
     elif collection_type == "file":
         result = self._files
     elif collection_type == "settings":
         result = self._settings
     else:
         raise CX("internal error, collection name %s not supported" %
                  collection_type)
     return result
Example #20
0
def run(api, args, logger):
    """
    The list of args should have one element:
        - 1: the name of the system or profile

    :param api: The api to resolve metadata with.
    :param args: This should be a list as described above.
    :param logger: This parameter is unused currently.
    :return: "0" on success.
    """

    # FIXME: use the logger

    if len(args) < 3:
        raise CX("invalid invocation")

    name = args[1]

    settings = api.settings()
    anamon_enabled = str(settings.anamon_enabled)

    # Remove any files matched with the given glob pattern
    def unlink_files(globex):
        for f in glob.glob(globex):
            if os.path.isfile(f):
                try:
                    os.unlink(f)
                except OSError:
                    pass

    if str(anamon_enabled) in ["true", "1", "y", "yes"]:
        dirname = "/var/log/cobbler/anamon/%s" % name
        if os.path.isdir(dirname):
            unlink_files(os.path.join(dirname, "*"))

    # TODO - log somewhere that we cleared a systems anamon logs
    return 0
Example #21
0
    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_hash(
                        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))
Example #22
0
    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))
Example #23
0
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)
Example #24
0
    def run(self,
            adduser=None,
            addgroup=None,
            removeuser=None,
            removegroup=None):
        """
        Automate setfacl commands
        """

        ok = False
        if adduser:
            ok = True
            self.modacl(True, True, adduser)
        if addgroup:
            ok = True
            self.modacl(True, False, addgroup)
        if removeuser:
            ok = True
            self.modacl(False, True, removeuser)
        if removegroup:
            ok = True
            self.modacl(False, False, removegroup)
        if not ok:
            raise CX("no arguments specified, nothing to do")
Example #25
0
    def remove(self, name: str, with_delete: bool = True, with_sync: bool = True, with_triggers: bool = True,
               recursive: bool = False):
        """
        Remove element named 'name' from the collection

        :raises CX: In case the name of the object was not given.
        """
        name = name.lower()
        obj = self.find(name=name)

        if obj is None:
            raise CX("cannot delete an object that does not exist: %s" % name)

        if with_delete:
            if with_triggers:
                utils.run_triggers(self.api, obj, "/var/lib/cobbler/triggers/delete/system/pre/*", [])
            if with_sync:
                lite_sync = self.api.get_sync()
                lite_sync.remove_single_system(name)

        if obj.parent is not None and obj.name in obj.parent.children:
            obj.parent.children.remove(obj.name)
            # ToDo: Only serialize parent object, use:
            #       Use self.collection_mgr.serialize_one_item(obj.parent)
            self.api.serialize()

        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.api, obj, "/var/lib/cobbler/triggers/delete/system/post/*", [])
                utils.run_triggers(self.api, obj, "/var/lib/cobbler/triggers/change/*", [])
Example #26
0
    def check_if_valid(self):
        """
        Checks if the object is valid. This is the case if name, path, owner, group, and mode are set.
        Templates are only required for files if ``is_dir`` is true then template is not required.

        :raises CX: Raised in case a required argument is missing
        """
        if not self.name:
            raise CX("name is required")
        if not self.path:
            raise CX("path is required")
        if not self.owner:
            raise CX("owner is required")
        if not self.group:
            raise CX("group is required")
        if not self.mode:
            raise CX("mode is required")
        if not self.is_dir and self.template == "":
            raise CX("Template is required when not a directory")
Example #27
0
    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)
Example #28
0
    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)
Example #29
0
def run(api, args, logger):
    # FIXME: make everything use the logger

    settings = api.settings()

    # go no further if this feature is turned off
    if not str(settings.build_reporting_enabled).lower() in [
            "1", "yes", "y", "true"
    ]:
        return 0

    objtype = args[0]  # "target" or "profile"
    name = args[1]  # name of target or profile
    boot_ip = args[2]  # ip or "?"

    if objtype == "system":
        target = api.find_system(name)
    else:
        target = api.find_profile(name)

    # collapse the object down to a rendered datastructure
    target = utils.blender(api, False, target)

    if target == {}:
        raise CX("failure looking up target")

    to_addr = settings.build_reporting_email
    if to_addr == "":
        return 0

    # add the ability to specify an MTA for servers that don't run their own
    smtp_server = settings.build_reporting_smtp_server
    if smtp_server == "":
        smtp_server = "localhost"

    # use a custom from address or fall back to a reasonable default
    from_addr = settings.build_reporting_sender
    if from_addr == "":
        from_addr = "cobbler@%s" % settings.server

    subject = settings.build_reporting_subject
    if subject == "":
        subject = '[Cobbler] install complete '

    to_addr = ", ".join(to_addr)
    metadata = {
        "from_addr": from_addr,
        "to_addr": to_addr,
        "subject": subject,
        "boot_ip": boot_ip
    }
    metadata.update(target)

    input_template = open("/etc/cobbler/reporting/build_report_email.template")
    input_data = input_template.read()
    input_template.close()

    message = templar.Templar().render(input_data, metadata, None)
    # for debug, call
    # print message

    # Send the mail
    # FIXME: on error, return non-zero
    server_handle = smtplib.SMTP(smtp_server)
    server_handle.sendmail(from_addr, to_addr, message)
    server_handle.quit()

    return 0
Example #30
0
def run(api, args):
    """
    This method executes the trigger, meaning in this case that it updates the dns configuration.

    :param api: The api to read metadata from.
    :param args: Metadata to log.
    :return: "0" on success or a skipped task. If the task failed or problems occurred then an exception is raised.
    """
    global logf

    action = None
    if __name__ == "cobbler.modules.nsupdate_add_system_post":
        action = "replace"
    elif __name__ == "cobbler.modules.nsupdate_delete_system_pre":
        action = "delete"
    else:
        return 0

    settings = api.settings()

    if not settings.nsupdate_enabled:
        return 0

    # read our settings
    if str(settings.nsupdate_log) is not None:
        logf = open(str(settings.nsupdate_log), "a+")
        nslog(">> starting %s %s\n" % (__name__, args))

    if str(settings.nsupdate_tsig_key) is not None:
        keyring = dns.tsigkeyring.from_text({
            str(settings.nsupdate_tsig_key[0]): str(settings.nsupdate_tsig_key[1])
        })
    else:
        keyring = None

    if str(settings.nsupdate_tsig_algorithm) is not None:
        keyring_algo = str(settings.nsupdate_tsig_algorithm)
    else:
        keyring_algo = "HMAC-MD5.SIG-ALG.REG.INT"
    # nslog( " algo %s, key %s : %s \n" % (keyring_algo,str(settings.nsupdate_tsig_key[0]),
    #                                      str(settings.nsupdate_tsig_key[1])) )

    # get information about this system
    system = api.find_system(args[0])

    # process all interfaces and perform dynamic update for those with --dns-name
    for (name, interface) in system.interfaces.items():
        host = interface.dns_name
        host_ip = interface.ip_address

        if not system.is_management_supported(cidr_ok=False):
            continue
        if not host:
            continue
        if host.find(".") == -1:
            continue

        domain = ".".join(host.split(".")[1:])    # get domain from host name
        host = host.split(".")[0]                   # strip domain

        nslog("processing interface %s : %s\n" % (name, interface))
        nslog("lookup for '%s' domain master nameserver... " % domain)

        # get master nameserver ip address
        answers = dns.resolver.query(domain + '.', dns.rdatatype.SOA)
        soa_mname = answers[0].mname
        soa_mname_ip = None

        for rrset in answers.response.additional:
            if rrset.name == soa_mname:
                soa_mname_ip = str(rrset.items[0].address)

        if soa_mname_ip is None:
            ip = dns.resolver.query(soa_mname, "A")
            for answer in ip:
                soa_mname_ip = answer.to_text()

        nslog("%s [%s]\n" % (soa_mname, soa_mname_ip))
        nslog("%s dns record for %s.%s [%s] .. " % (action, host, domain, host_ip))

        # try to update zone with new record
        update = dns.update.Update(domain + '.', keyring=keyring, keyalgorithm=keyring_algo)

        if action == "replace":
            update.replace(host, 3600, dns.rdatatype.A, host_ip)
            update.replace(host, 3600, dns.rdatatype.TXT, '"cobbler (date: %s)"' % (time.strftime("%c")))
        else:
            update.delete(host, dns.rdatatype.A, host_ip)
            update.delete(host, dns.rdatatype.TXT)

        try:
            response = dns.query.tcp(update, soa_mname_ip)
            rcode_txt = dns.rcode.to_text(response.rcode())
        except dns.tsig.PeerBadKey:
            nslog("failed (refused key)\n>> done\n")
            logf.close()

            raise CX("nsupdate failed, server '%s' refusing our key" % soa_mname)

        nslog('response code: %s\n' % rcode_txt)

        # notice user about update failure
        if response.rcode() != dns.rcode.NOERROR:
            nslog('>> done\n')
            logf.close()

            raise CX("nsupdate failed (response: %s, name: %s.%s, ip %s, name server %s)"
                     % (rcode_txt, host, domain, host_ip, soa_mname))

    nslog('>> done\n')
    logf.close()
    return 0