Source code for cobbler.services

"""
Mod Python service functions for Cobbler's public interface (aka cool stuff that works with wget/curl)
"""

from future import standard_library
standard_library.install_aliases()
from builtins import str
from builtins import object
import simplejson
import time
import xmlrpc.client
import yaml
from cobbler.cobbler_collections import manager
from cobbler import download_manager


[docs]class CobblerSvc(object): """ Interesting mod python functions are all keyed off the parameter mode, which defaults to index. All options are passed as parameters into the function. """ def __init__(self, server=None, req=None): self.server = server self.remote = None self.req = req self.collection_mgr = manager.CollectionManager(self) self.logger = None self.dlmgr = download_manager.DownloadManager(self.collection_mgr, self.logger) def __xmlrpc_setup(self): """ Sets up the connection to the Cobbler XMLRPC server. This is the version that does not require logins. """ if self.remote is None: self.remote = xmlrpc.client.Server(self.server, allow_none=True)
[docs] def index(self, **args): return "no mode specified"
[docs] def debug(self, profile=None, **rest): # the purpose of this method could change at any time # and is intented for temporary test code only, don't rely on it self.__xmlrpc_setup() return self.remote.get_repos_compatible_with_profile(profile)
[docs] def autoinstall(self, profile=None, system=None, REMOTE_ADDR=None, REMOTE_MAC=None, **rest): """ Generate automatic installation files """ self.__xmlrpc_setup() data = self.remote.generate_autoinstall(profile, system, REMOTE_ADDR, REMOTE_MAC) return "%s" % data
[docs] def ks(self, profile=None, system=None, REMOTE_ADDR=None, REMOTE_MAC=None, **rest): """ Generate automatic installation files. This is a legacy function for part backward compability to 2.6.6 releases. :param profile: :param system: :param REMOTE_ADDR: :param REMOTE_MAC: :param rest: :return: """ self.__xmlrpc_setup() data = self.remote.generate_autoinstall(profile, system, REMOTE_ADDR, REMOTE_MAC) return "%s" % data
[docs] def gpxe(self, profile=None, system=None, mac=None, **rest): """ Generate a gPXE config """ self.__xmlrpc_setup() if not system and mac: query = {"mac_address": mac} if profile: query["profile"] = profile found = self.remote.find_system(query) if found: system = found[0] data = self.remote.generate_gpxe(profile, system) return "%s" % data
[docs] def bootcfg(self, profile=None, system=None, **rest): """ Generate a boot.cfg config file. Used primarily for VMware ESXi. """ self.__xmlrpc_setup() data = self.remote.generate_bootcfg(profile, system) return "%s" % data
[docs] def script(self, profile=None, system=None, **rest): """ Generate a script based on snippets. Useful for post or late-action scripts where it's difficult to embed the script in the response file. """ self.__xmlrpc_setup() data = self.remote.generate_script(profile, system, rest['query_string']['script'][0]) return "%s" % data
[docs] def events(self, user="", **rest): self.__xmlrpc_setup() if user == "": data = self.remote.get_events("") else: data = self.remote.get_events(user) # sort it... it looks like { timestamp : [ array of details ] } keylist = list(data.keys()) keylist.sort() results = [] for k in keylist: etime = int(data[k][0]) nowtime = time.time() if ((nowtime - etime) < 30): results.append([k, data[k][0], data[k][1], data[k][2]]) return simplejson.dumps(results)
[docs] def template(self, profile=None, system=None, path=None, **rest): """ Generate a templated file for the system """ self.__xmlrpc_setup() if path is not None: path = path.replace("_", "/") path = path.replace("//", "_") else: return "# must specify a template path" if profile is not None: data = self.remote.get_template_file_for_profile(profile, path) elif system is not None: data = self.remote.get_template_file_for_system(system, path) else: data = "# must specify profile or system name" return data
[docs] def yum(self, profile=None, system=None, **rest): self.__xmlrpc_setup() if profile is not None: data = self.remote.get_repo_config_for_profile(profile) elif system is not None: data = self.remote.get_repo_config_for_system(system) else: data = "# must specify profile or system name" return data
[docs] def trig(self, mode="?", profile=None, system=None, REMOTE_ADDR=None, **rest): """ Hook to call install triggers. """ self.__xmlrpc_setup() ip = REMOTE_ADDR if profile: rc = self.remote.run_install_triggers(mode, "profile", profile, ip) else: rc = self.remote.run_install_triggers(mode, "system", system, ip) return str(rc)
[docs] def nopxe(self, system=None, **rest): self.__xmlrpc_setup() return str(self.remote.disable_netboot(system))
[docs] def list(self, what="systems", **rest): self.__xmlrpc_setup() buf = "" if what == "systems": listing = self.remote.get_systems() elif what == "profiles": listing = self.remote.get_profiles() elif what == "distros": listing = self.remote.get_distros() elif what == "images": listing = self.remote.get_images() elif what == "repos": listing = self.remote.get_repos() elif what == "mgmtclasses": listing = self.remote.get_mgmtclasses() elif what == "packages": listing = self.remote.get_packages() elif what == "files": listing = self.remote.get_files() else: return "?" for x in listing: buf += "%s\n" % x["name"] return buf
[docs] def autodetect(self, **rest): self.__xmlrpc_setup() systems = self.remote.get_systems() # if kssendmac was in the kernel options line, see # if a system can be found matching the MAC address. This # is more specific than an IP match. macinput = [mac.split(' ').lower() for mac in rest["REMOTE_MACS"]] ip = rest["REMOTE_ADDR"] candidates = [] for x in systems: for y in x["interfaces"]: if x["interfaces"][y]["mac_address"].lower() in macinput: candidates.append(x) if len(candidates) == 0: for x in systems: for y in x["interfaces"]: if x["interfaces"][y]["ip_address"] == ip: candidates.append(x) if len(candidates) == 0: return "FAILED: no match (%s,%s)" % (ip, macinput) elif len(candidates) > 1: return "FAILED: multiple matches" elif len(candidates) == 1: return candidates[0]["name"]
[docs] def look(self, **rest): # debug only return repr(rest)
[docs] def find_autoinstall(self, system=None, profile=None, **rest): self.__xmlrpc_setup() serverseg = "http://%s" % self.collection_mgr._settings.server name = "?" if system is not None: url = "%s/cblr/svc/op/autoinstall/system/%s" % (serverseg, name) elif profile is not None: url = "%s/cblr/svc/op/autoinstall/profile/%s" % (serverseg, name) else: name = self.autodetect(**rest) if name.startswith("FAILED"): return "# autodetection %s" % name url = "%s/cblr/svc/op/autoinstall/system/%s" % (serverseg, name) try: return self.dlmgr.urlread(url) except: return "# automatic installation file retrieval failed (%s)" % url
[docs] def findks(self, system=None, profile=None, **rest): """ This is a legacy function which enabled cobbler partly to be backward compatible to 2.6.6 releases. It should be only be used if you must. Please use find_autoinstall if possible! :param system: If you wish to find a system please set this parameter to not null. Hand over the name of it. :param profile: If you wish to find a system please set this parameter to not null. Hand over the name of it. :param rest: If you wish you can try to let cobbler autodetect the system with the MAC address. :return: Returns the autoinstall/kickstart profile. """ self.__xmlrpc_setup() serverseg = "http://%s" % self.collection_mgr._settings.server name = "?" if system is not None: url = "%s/cblr/svc/op/ks/system/%s" % (serverseg, name) elif profile is not None: url = "%s/cblr/svc/op/ks/profile/%s" % (serverseg, name) else: name = self.autodetect(**rest) if name.startswith("FAILED"): return "# autodetection %s" % name url = "%s/cblr/svc/op/ks/system/%s" % (serverseg, name) try: return self.dlmgr.urlread(url) except: return "# kickstart retrieval failed (%s)" % url
[docs] def puppet(self, hostname=None, **rest): self.__xmlrpc_setup() if hostname is None: return "hostname is required" settings = self.remote.get_settings() results = self.remote.find_system_by_dns_name(hostname) classes = results.get("mgmt_classes", {}) params = results.get("mgmt_parameters", {}) environ = results.get("status", "") data = { "classes": classes, "parameters": params, "environment": environ, } if environ == "": data.pop("environment", None) if settings.get("puppet_parameterized_classes", False): for ckey in list(classes.keys()): tmp = {} class_name = classes[ckey].get("class_name", "") if class_name in (None, ""): class_name = ckey if classes[ckey].get("is_definition", False): def_tmp = {} def_name = classes[ckey]["params"].get("name", "") del classes[ckey]["params"]["name"] if def_name != "": for pkey in list(classes[ckey]["params"].keys()): def_tmp[pkey] = classes[ckey]["params"][pkey] tmp["instances"] = {def_name: def_tmp} else: # FIXME: log an error here? # skip silently... continue else: for pkey in list(classes[ckey]["params"].keys()): tmp[pkey] = classes[ckey]["params"][pkey] del classes[ckey] classes[class_name] = tmp else: classes = list(classes.keys()) return yaml.dump(data, default_flow_style=False)