summaryrefslogtreecommitdiffstats
path: root/cobbler/remote.py
diff options
context:
space:
mode:
Diffstat (limited to 'cobbler/remote.py')
-rw-r--r--cobbler/remote.py448
1 files changed, 288 insertions, 160 deletions
diff --git a/cobbler/remote.py b/cobbler/remote.py
index ff8d2db..57570aa 100644
--- a/cobbler/remote.py
+++ b/cobbler/remote.py
@@ -20,10 +20,10 @@ import os
import SimpleXMLRPCServer
from rhpl.translate import _, N_, textdomain, utf8
import xmlrpclib
-import logging
-import ConfigParser
import random
import base64
+import string
+import traceback
import api as cobbler_api
import utils
@@ -33,17 +33,11 @@ import item_profile
import item_system
import item_repo
-config_parser = ConfigParser.ConfigParser()
-auth_conf = open("/etc/cobbler/auth.conf")
-config_parser.readfp(auth_conf)
-auth_conf.close()
-
-user_database = config_parser.items("xmlrpc_service_users")
-
-
# FIXME: make configurable?
TOKEN_TIMEOUT = 60*60 # 60 minutes
OBJECT_TIMEOUT = 60*60 # 60 minutes
+TOKEN_CACHE = {}
+OBJECT_CACHE = {}
# *********************************************************************
# *********************************************************************
@@ -59,9 +53,10 @@ class CobblerXMLRPCInterface:
interface are intentionally /not/ validated. It's a public API.
"""
- def __init__(self,api,logger):
+ def __init__(self,api,logger,enable_auth_if_relevant):
self.api = api
self.logger = logger
+ self.auth_enabled = enable_auth_if_relevant
def __sorter(self,a,b):
return cmp(a["name"],b["name"])
@@ -69,6 +64,55 @@ class CobblerXMLRPCInterface:
def ping(self):
return True
+ def get_user_from_token(self,token):
+ if not TOKEN_CACHE.has_key(token):
+ raise CX(_("invalid token: %s") % token)
+ else:
+ return self.token_cache[token][1]
+
+ def log(self,msg,user=None,token=None,name=None,object_id=None,attribute=None,debug=False,error=False):
+
+ # add the user editing the object, if supplied
+ m_user = "?"
+ if user is not None:
+ m_user = user
+ if token is not None:
+ try:
+ m_user = self.get_user_from_token(token)
+ except:
+ # invalid or expired token?
+ m_user = "???"
+ msg = "%s; user(%s)" % (msg, m_user)
+
+ # add the object name being modified, if any
+ oname = ""
+ if name:
+ oname = name
+ elif object_id:
+ try:
+ (objref, time) = self.object_cache[object_id]
+ oname = objref.name
+ if oname == "" or oname is None:
+ oname = "???"
+ except:
+ oname = "*EXPIRED*"
+ if oname != "":
+ msg = "%s; object(%s)" % (msg, oname)
+
+
+ # add any attributes being modified, if any
+ if attribute:
+ msg = "%s; attribute(%s)" % (msg, attribute)
+
+ # log to the correct logger
+ if error:
+ logger = self.logger.error
+ elif debug:
+ logger = self.logger.debug
+ else:
+ logger = self.logger.info
+ logger(msg)
+
def get_size(self,collection_name):
"""
Returns the number of entries in a collection (but not the actual
@@ -100,16 +144,12 @@ class CobblerXMLRPCInterface:
if page is not None and results_per_page is not None:
page = int(page)
results_per_page = int(results_per_page)
- self.logger.debug("PAGE = %s" % page)
- self.logger.debug("RPP = %s" % results_per_page)
if page < 0:
return []
if results_per_page <= 0:
return []
start_point = (results_per_page * page)
end_point = (results_per_page * page) + results_per_page
- self.logger.debug("START = %s" % start_point)
- self.logger.debug("END = %s" % end_point)
if start_point > total_items:
start_point = total_items - 1 # correct ???
if end_point > total_items:
@@ -122,6 +162,7 @@ class CobblerXMLRPCInterface:
"""
Return the contents of /var/lib/cobbler/settings, which is a hash.
"""
+ self.log("get_settings",token=token)
return self.__get_all("settings")
def disable_netboot(self,name,token=None):
@@ -130,6 +171,7 @@ class CobblerXMLRPCInterface:
Sets system named "name" to no-longer PXE. Disabled by default as
this requires public API access and is technically a read-write operation.
"""
+ self.log("disable_netboot",token=token,name=name)
# used by nopxe.cgi
self.api.clear()
self.api.deserialize()
@@ -142,7 +184,30 @@ class CobblerXMLRPCInterface:
# system not found!
return False
obj.set_netboot_enabled(0)
- systems.add(obj,with_copy=True)
+ # disabling triggers and sync to make this extremely fast.
+ systems.add(obj,save=True,with_triggers=False,with_sync=False,quick_pxe_update=True)
+ return True
+
+ def run_post_install_triggers(self,name,token=None):
+ """
+ This is a feature used to run the post install trigger.
+ It passes the system named "name" to the trigger. Disabled by default as
+ this requires public API access and is technically a read-write operation.
+ """
+ self.log("run_post_install_triggers",token=token)
+
+ # used by postinstalltrigger.cgi
+ self.api.clear()
+ self.api.deserialize()
+ if not self.api.settings().run_post_install_trigger:
+ # feature disabled!
+ return False
+ systems = self.api.systems()
+ obj = systems.find(name=name)
+ if obj == None:
+ # system not found!
+ return False
+ utils.run_triggers(obj, "/var/lib/cobbler/triggers/install/post/*")
return True
def _refresh(self):
@@ -160,30 +225,35 @@ class CobblerXMLRPCInterface:
Return the cobbler version for compatibility testing with remote applications.
Returns as a float, 0.6.1-2 should result in (int) "0.612".
"""
+ self.log("version",token=token)
return self.api.version()
def get_distros(self,page=None,results_per_page=None,token=None):
"""
Returns all cobbler distros as an array of hashes.
"""
+ self.log("get_distros",token=token)
return self.__get_all("distro",page,results_per_page)
def get_profiles(self,page=None,results_per_page=None,token=None):
"""
Returns all cobbler profiles as an array of hashes.
"""
+ self.log("get_profiles",token=token)
return self.__get_all("profile",page,results_per_page)
def get_systems(self,page=None,results_per_page=None,token=None):
"""
Returns all cobbler systems as an array of hashes.
"""
+ self.log("get_systems",token=token)
return self.__get_all("system",page,results_per_page)
def get_repos(self,page=None,results_per_page=None,token=None):
"""
Returns all cobbler repos as an array of hashes.
"""
+ self.log("get_repos",token=token)
return self.__get_all("repo",page,results_per_page)
def __get_specific(self,collection_fn,name,flatten=False):
@@ -204,24 +274,28 @@ class CobblerXMLRPCInterface:
"""
Returns the distro named "name" as a hash.
"""
+ self.log("get_distro",token=token,name=name)
return self.__get_specific(self.api.distros,name,flatten=flatten)
def get_profile(self,name,flatten=False,token=None):
"""
Returns the profile named "name" as a hash.
"""
+ self.log("get_profile",token=token,name=name)
return self.__get_specific(self.api.profiles,name,flatten=flatten)
def get_system(self,name,flatten=False,token=None):
"""
Returns the system named "name" as a hash.
"""
+ self.log("get_system",name=name,token=token)
return self.__get_specific(self.api.systems,name,flatten=flatten)
def get_repo(self,name,flatten=False,token=None):
"""
Returns the repo named "name" as a hash.
"""
+ self.log("get_repo",name=name,token=token)
return self.__get_specific(self.api.repos,name,flatten=flatten)
def get_distro_as_rendered(self,name,token=None):
@@ -236,6 +310,7 @@ class CobblerXMLRPCInterface:
"""
Same as get_distro_as_rendered.
"""
+ self.log("get_distro_as_rendered",name=name,token=token)
self._refresh()
obj = self.api.distros().find(name=name)
if obj is not None:
@@ -254,6 +329,7 @@ class CobblerXMLRPCInterface:
"""
Same as get_profile_as_rendered
"""
+ self.log("get_profile_as_rendered", name=name, token=token)
self._refresh()
obj = self.api.profiles().find(name=name)
if obj is not None:
@@ -272,6 +348,7 @@ class CobblerXMLRPCInterface:
"""
Same as get_system_as_rendered.
"""
+ self.log("get_system_as_rendered",name=name,token=token)
self._refresh()
obj = self.api.systems().find(name=name)
if obj is not None:
@@ -290,13 +367,14 @@ class CobblerXMLRPCInterface:
"""
Same as get_repo_as_rendered.
"""
+ self.log("get_repo_as_rendered",name=name,token=token)
self._refresh()
obj = self.api.repos().find(name=name)
if obj is not None:
return self._fix_none(utils.blender(self.api, True, obj))
return self._fix_none({})
- def get_random_mac(self):
+ def get_random_mac(self,token=None):
"""
Generate a random MAC address.
from xend/server/netif.py
@@ -305,6 +383,7 @@ class CobblerXMLRPCInterface:
Xensource, Inc. Last 3 fields are random.
return: MAC address string
"""
+ self.log("get_random_mac",token=None)
self._refresh()
mac = [ 0x00, 0x16, 0x3e,
random.randint(0x00, 0x7f),
@@ -346,13 +425,37 @@ class CobblerXMLRPCServer(SimpleXMLRPCServer.SimpleXMLRPCServer):
# *********************************************************************************
# *********************************************************************************
+
+class ProxiedXMLRPCInterface:
+
+ def __init__(self,api,logger,proxy_class,enable_auth_if_relevant=True):
+ self.logger = logger
+ self.proxied = proxy_class(api,logger,enable_auth_if_relevant)
+
+ def _dispatch(self, method, params):
+
+ if not hasattr(self.proxied, method):
+ self.logger.error("remote:unknown method %s" % method)
+ raise CX(_("Unknown remote method"))
+
+ method_handle = getattr(self.proxied, method)
+
+ try:
+ return method_handle(*params)
+ except Exception, e:
+ utils.log_exc(self.logger)
+ raise e
+
+# **********************************************************************
+
class CobblerReadWriteXMLRPCInterface(CobblerXMLRPCInterface):
- def __init__(self,api,logger):
+ def __init__(self,api,logger,enable_auth_if_relevant):
self.api = api
+ self.auth_enabled = enable_auth_if_relevant
self.logger = logger
- self.token_cache = {}
- self.object_cache = {}
+ self.token_cache = TOKEN_CACHE
+ self.object_cache = OBJECT_CACHE
random.seed(time.time())
def __next_id(self,retry=0):
@@ -374,12 +477,12 @@ class CobblerReadWriteXMLRPCInterface(CobblerXMLRPCInterface):
urandom.close()
return b64
- def __make_token(self):
+ def __make_token(self,user):
"""
Returns a new random token.
"""
b64 = self.__get_random(25)
- self.token_cache[b64] = time.time()
+ self.token_cache[b64] = (time.time(), user)
return b64
def __invalidate_expired_objects(self):
@@ -391,7 +494,7 @@ class CobblerReadWriteXMLRPCInterface(CobblerXMLRPCInterface):
for object_id in self.object_cache.keys():
(reference, object_time) = self.object_cache[object_id]
if (timenow > object_time + OBJECT_TIMEOUT):
- self.logger.debug("expiring object reference: %s" % id)
+ self.log("expiring object reference: %s" % id,debug=True)
del self.object_cache[object_id]
def __invalidate_expired_tokens(self):
@@ -400,27 +503,27 @@ class CobblerReadWriteXMLRPCInterface(CobblerXMLRPCInterface):
"""
timenow = time.time()
for token in self.token_cache.keys():
- tokentime = self.token_cache[token]
+ (tokentime, user) = self.token_cache[token]
if (timenow > tokentime + TOKEN_TIMEOUT):
- self.logger.debug("expiring token: %s" % token)
+ self.log("expiring token",token=token,debug=True)
del self.token_cache[token]
- def __validate_user(self,user,password):
+ def __validate_user(self,input_user,input_password):
"""
Returns whether this user/pass combo should be given
access to the cobbler read-write API.
+ For the system user, this answer is always "yes", but
+ it is only valid for the socket interface.
+
FIXME: currently looks for users in /etc/cobbler/auth.conf
Would be very nice to allow for PAM and/or just Kerberos.
"""
- for x in user_database:
- (db_user,db_password) = x
- db_user = db_user.strip()
- db_password = db_password.strip()
- if db_user == user and db_password == password and db_password.lower() != "disabled":
- return True
- else:
+ if not self.auth_enabled and input_user == "<system>":
+ return True
+ if self.auth_enabled and input_user == "<system>":
return False
+ return self.api.authenticate(input_user,input_password)
def __validate_token(self,token):
"""
@@ -433,32 +536,59 @@ class CobblerReadWriteXMLRPCInterface(CobblerXMLRPCInterface):
"""
self.__invalidate_expired_tokens()
self.__invalidate_expired_objects()
+
+ if not self.auth_enabled:
+ user = self.get_user_from_token(token)
+ if user == "<system>":
+ self.token_cache[token] = (time.time(), user) # update to prevent timeout
+ return True
+
if self.token_cache.has_key(token):
- self.token_cache[token] = time.time() # update to prevent timeout
+ user = self.get_user_from_token(token)
+ if user == "<system>":
+ # system token is only valid over Unix socket
+ return False
+ self.token_cache[token] = (time.time(), user) # update to prevent timeout
return True
else:
- self.logger.debug("invalid token: %s" % token)
+ self.log("invalid token",token=token)
raise CX(_("invalid token: %s" % token))
- def login(self,user,password):
+ def check_access(self,token,resource,arg1=None,arg2=None):
+ validated = self.__validate_token(token)
+ if not self.auth_enabled:
+ return True
+ return self.__authorize(token,resource,arg1,arg2)
+
+
+ def login(self,login_user,login_password):
"""
Takes a username and password, validates it, and if successful
returns a random login token which must be used on subsequent
method calls. The token will time out after a set interval if not
used. Re-logging in permitted.
"""
- if self.__validate_user(user,password):
- token = self.__make_token()
- self.logger.info("login succeeded: %s" % user)
+ self.log("login attempt", user=login_user)
+ if self.__validate_user(login_user,login_password):
+ token = self.__make_token(login_user)
+ self.log("login succeeded",user=login_user)
return token
else:
- self.logger.info("login failed: %s" % user)
- raise CX(_("login failed: %s") % user)
+ self.log("login failed",user=login_user)
+ raise CX(_("login failed: %s") % login_user)
+
+ def __authorize(self,token,resource,arg1=None,arg2=None):
+ user = self.get_user_from_token(token)
+ if self.api.authorize(user,resource,arg1,arg2):
+ return True
+ else:
+ raise CX(_("user does not have access to resource: %s") % resource)
def logout(self,token):
"""
Retires a token ahead of the timeout.
"""
+ self.log("logout", token=token)
if self.token_cache.has_key(token):
del self.token_cache[token]
return True
@@ -503,7 +633,8 @@ class CobblerReadWriteXMLRPCInterface(CobblerXMLRPCInterface):
remote.modify_distro(distro_id, 'initrd', '/foo/initrd.img', token)
remote.save_distro(distro_id, token)
"""
- self.__validate_token(token)
+ self.log("new_distro",token=token)
+ self.check_access(token,"new_distro")
return self.__store_object(item_distro.Distro(self.api._config))
def new_profile(self,token):
@@ -511,19 +642,12 @@ class CobblerReadWriteXMLRPCInterface(CobblerXMLRPCInterface):
Creates a new (unconfigured) profile object. See the documentation
for new_distro as it works exactly the same.
"""
- self.__validate_token(token)
+ self.log("new_profile",token=token)
+ self.check_access(token,"new_profile")
return self.__store_object(item_profile.Profile(self.api._config))
def new_subprofile(self,token):
"""
- Creates a new (unconfigured) subprofile object. See the documentation
- for new_distro as it works exactly the same.
- """
- self.__validate_token(token)
- return self.__store_object(item_profile.Profile(self.api._config, is_subobject=True))
-
- def new_subprofile(self,token):
- """
A subprofile is a profile that inherits directly from another profile,
not a distro. In addition to the normal profile setup, setting
the parent variable to the name of an existing profile is also
@@ -531,7 +655,8 @@ class CobblerReadWriteXMLRPCInterface(CobblerXMLRPCInterface):
were regular profiles. The same XMLRPC API methods work on them as profiles
also.
"""
- self.__validate_token(token)
+ self.log("new_subprofile",token=token)
+ self.check_access(token,"new_subprofile")
return self.__store_object(item_profile.Profile(self.api._config,is_subobject=True))
def new_system(self,token):
@@ -539,7 +664,8 @@ class CobblerReadWriteXMLRPCInterface(CobblerXMLRPCInterface):
Creates a new (unconfigured) system object. See the documentation
for new_distro as it works exactly the same.
"""
- self.__validate_token(token)
+ self.log("new_system",token=token)
+ self.check_access(token,"new_system")
return self.__store_object(item_system.System(self.api._config))
def new_repo(self,token):
@@ -547,7 +673,8 @@ class CobblerReadWriteXMLRPCInterface(CobblerXMLRPCInterface):
Creates a new (unconfigured) repo object. See the documentation
for new_distro as it works exactly the same.
"""
- self.__validate_token(token)
+ self.log("new_repo",token=token)
+ self.check_access(token,"new_repo")
return self.__store_object(item_repo.Repo(self.api._config))
def get_distro_handle(self,name,token):
@@ -556,7 +683,8 @@ class CobblerReadWriteXMLRPCInterface(CobblerXMLRPCInterface):
object id that can be passed in to modify_distro() or save_distro()
commands. Raises an exception if no object can be matched.
"""
- self.__validate_token(token)
+ self.log("get_distro_handle",token=token,name=name)
+ self.check_access(token,"get_distro_handle")
self._refresh()
found = self.api.distros().find(name)
return self.__store_object(found)
@@ -567,7 +695,8 @@ class CobblerReadWriteXMLRPCInterface(CobblerXMLRPCInterface):
object id that can be passed in to modify_profile() or save_profile()
commands. Raises an exception if no object can be matched.
"""
- self.__validate_token(token)
+ self.log("get_profile_handle",token=token,name=name)
+ self.check_access(token,"get_profile_handle")
self._refresh()
found = self.api.profiles().find(name)
return self.__store_object(found)
@@ -578,7 +707,8 @@ class CobblerReadWriteXMLRPCInterface(CobblerXMLRPCInterface):
object id that can be passed in to modify_system() or save_system()
commands. Raises an exception if no object can be matched.
"""
- self.__validate_token(token)
+ self.log("get_system_handle",name=name,token=token)
+ self.check_access(token,"get_system_handle")
self._refresh()
found = self.api.systems().find(name)
return self.__store_object(found)
@@ -589,7 +719,8 @@ class CobblerReadWriteXMLRPCInterface(CobblerXMLRPCInterface):
object id that can be passed in to modify_repo() or save_pro()
commands. Raises an exception if no object can be matched.
"""
- self.__validate_token(token)
+ self.log("get_repo_handle",name=name,token=token)
+ self.check_access(token,"get_repo_handle")
self._refresh()
found = self.api.repos().find(name)
return self.__store_object(found)
@@ -598,33 +729,94 @@ class CobblerReadWriteXMLRPCInterface(CobblerXMLRPCInterface):
"""
Saves a newly created or modified distro object to disk.
"""
- self.__validate_token(token)
+ self.log("save_distro",object_id=object_id,token=token)
+ self.check_access(token,"save_distro")
obj = self.__get_object(object_id)
- return self.api.distros().add(obj,with_copy=True)
+ return self.api.distros().add(obj,save=True)
def save_profile(self,object_id,token):
"""
Saves a newly created or modified profile object to disk.
"""
- self.__validate_token(token)
+ self.log("save_profile",token=token,object_id=object_id)
+ self.check_access(token,"save_profile")
obj = self.__get_object(object_id)
- return self.api.profiles().add(obj,with_copy=True)
+ return self.api.profiles().add(obj,save=True)
def save_system(self,object_id,token):
"""
Saves a newly created or modified system object to disk.
"""
- self.__validate_token(token)
+ self.log("save_system",token=token,object_id=object_id)
+ self.check_access(token,"save_system")
obj = self.__get_object(object_id)
- return self.api.systems().add(obj,with_copy=True)
+ return self.api.systems().add(obj,save=True)
def save_repo(self,object_id,token=None):
"""
Saves a newly created or modified repo object to disk.
"""
- self.__validate_token(token)
+ self.log("save_repo",object_id=object_id,token=token)
+ self.check_access(token,"save_repo")
+ obj = self.__get_object(object_id)
+ return self.api.repos().add(obj,save=True)
+
+ def copy_distro(self,object_id,newname,token=None):
+ """
+ All copy methods are pretty much the same. Get an object handle, pass in the new
+ name for it.
+ """
+ self.log("copy_distro",object_id=object_id,token=token)
+ self.check_access(token,"copy_distro")
+ obj = self.__get_object(object_id)
+ return self.api.copy_distro(obj,newname)
+
+ def copy_profile(self,object_id,token=None):
+ self.log("copy_profile",object_id=object_id,token=token)
+ self.check_access(token,"copy_profile")
obj = self.__get_object(object_id)
- return self.api.repos().add(obj,with_copy=True)
+ return self.api.copy_profile(obj,newname)
+
+ def copy_system(self,object_id,token=None):
+ self.log("copy_system",object_id=object_id,token=token)
+ self.check_access(token,"copy_system")
+ obj = self.__get_object(object_id)
+ return self.api.copy_system(obj,newname)
+
+ def copy_repo(self,object_id,token=None):
+ self.log("copy_repo",object_id=object_id,token=token)
+ self.check_access(token,"copy_repo")
+ obj = self.__get_object(object_id)
+ return self.api.copy_repo(obj,newname)
+
+ def rename_distro(self,object_id,newname,token=None):
+ """
+ All rename methods are pretty much the same. Get an object handle, pass in a new
+ name for it. Rename will modify dependencies to point them at the new
+ object.
+ """
+ self.log("rename_distro",object_id=object_id,token=token)
+ self.check_access(token,"copy_repo")
+ obj = self.__get_object(object_id)
+ return self.api.rename_distro(obj,newname)
+
+ def rename_profile(self,object_id,newname,token=None):
+ self.log("rename_profile",object_id=object_id,token=token)
+ self.check_access(token,"rename_profile")
+ obj = self.__get_object(object_id)
+ return self.api.rename_profile(obj,newname)
+
+ def rename_system(self,object_id,newname,token=None):
+ self.log("rename_system",object_id=object_id,token=token)
+ self.check_access(token,"rename_system")
+ obj = self.__get_object(object_id)
+ return self.api.rename_system(obj,newname)
+
+ def rename_repo(self,object_id,newname,token=None):
+ self.log("rename_repo",object_id=object_id,token=token)
+ self.check_access(token,"rename_repo")
+ obj = self.__get_object(object_id)
+ return self.api.rename_repo(obj,newname)
def __call_method(self, obj, attribute, arg):
"""
@@ -640,7 +832,7 @@ class CobblerReadWriteXMLRPCInterface(CobblerXMLRPCInterface):
Allows modification of certain attributes on newly created or
existing distro object handle.
"""
- self.__validate_token(token)
+ self.check_access(token, "modify_distro", attribute, arg)
obj = self.__get_object(object_id)
return self.__call_method(obj, attribute, arg)
@@ -649,7 +841,7 @@ class CobblerReadWriteXMLRPCInterface(CobblerXMLRPCInterface):
Allows modification of certain attributes on newly created or
existing profile object handle.
"""
- self.__validate_token(token)
+ self.check_access(token, "modify_profile", attribute, arg)
obj = self.__get_object(object_id)
return self.__call_method(obj, attribute, arg)
@@ -658,7 +850,7 @@ class CobblerReadWriteXMLRPCInterface(CobblerXMLRPCInterface):
Allows modification of certain attributes on newly created or
existing system object handle.
"""
- self.__validate_token(token)
+ self.check_access(token, "modify_system", attribute, arg)
obj = self.__get_object(object_id)
return self.__call_method(obj, attribute, arg)
@@ -667,43 +859,47 @@ class CobblerReadWriteXMLRPCInterface(CobblerXMLRPCInterface):
Allows modification of certain attributes on newly created or
existing repo object handle.
"""
- self.__validate_token(token)
+ self.check_access(token, "modify_repo", attribute, arg)
obj = self.__get_object(object_id)
return self.__call_method(obj, attribute, arg)
- def distro_remove(self,name,token):
+ def remove_distro(self,name,token,recursive=1):
"""
Deletes a distro from a collection. Note that this just requires the name
of the distro, not a handle.
"""
- self.__validate_token(token)
- rc = self.api._config.distros().remove(name)
+ self.log("remove_distro",name=name,token=token)
+ self.check_access(token, "remove_distro", name)
+ rc = self.api._config.distros().remove(name,recursive=True)
return rc
- def profile_remove(self,name,token):
+ def remove_profile(self,name,token,recursive=1):
"""
Deletes a profile from a collection. Note that this just requires the name
of the profile, not a handle.
"""
- self.__validate_token(token)
- rc = self.api._config.profiles().remove(name)
+ self.log("remove_profile",name=name,token=token)
+ self.check_access(token, "remove_profile", name)
+ rc = self.api._config.profiles().remove(name,recursive=True)
return rc
- def system_remove(self,name,token):
+ def remove_system(self,name,token):
"""
Deletes a system from a collection. Note that this just requires the name
of the system, not a handle.
"""
- self.__validate_token(token)
+ self.log("remove_system",name=name,token=token)
+ self.check_access(token, "remove_system", name)
rc = self.api._config.systems().remove(name)
return rc
- def repo_remove(self,name,token):
+ def remove_repo(self,name,token):
"""
Deletes a repo from a collection. Note that this just requires the name
of the repo, not a handle.
"""
- self.__validate_token(token)
+ self.log("remove_repo",name=name,token=token)
+ self.check_access(token, "remove_repo", name)
rc = self.api._config.repos().remove(name)
return rc
@@ -718,7 +914,8 @@ class CobblerReadWriteXMLRPCInterface(CobblerXMLRPCInterface):
Future versions of cobbler may understand how to do a cascade sync
on object edits making explicit calls to sync redundant.
"""
- self.__validate_token(token)
+ self.log("sync",token=token)
+ self.check_access(token, "sync")
return self.api.sync()
def reposync(self,repos=[],token=None):
@@ -727,7 +924,8 @@ class CobblerReadWriteXMLRPCInterface(CobblerXMLRPCInterface):
reposync is very slow and probably should not be used
through the XMLRPC API, setting up reposync on nightly cron is better.
"""
- self.__validate_token(token)
+ self.log("reposync",token=token,name=repos)
+ self.check_access(token, "reposync", repos)
return self.api.reposync(repos)
def import_tree(self,mirror_url,mirror_name,network_root=None,token=None):
@@ -737,14 +935,16 @@ class CobblerReadWriteXMLRPCInterface(CobblerXMLRPCInterface):
It would be better to use the CLI. See documentation in api.py.
This command may be removed from the API in a future release.
"""
- self.__validate_token(token)
+ self.log("import_tree",name=mirror_name,token=token)
+ self.check_access(token, "import_tree")
return self.api.import_tree(mirror_url,mirror_name,network_root)
def get_kickstart_templates(self,token):
"""
Returns all of the kickstarts that are in use by the system.
"""
- self.__validate_token(token)
+ self.log("get_kickstart_templates",token=token)
+ self.check_access(token, "get_kickstart_templates")
files = {}
for x in self.api.profiles():
if x.kickstart is not None and x.kickstart != "" and x.kickstart != "<<inherit>>":
@@ -764,7 +964,8 @@ class CobblerReadWriteXMLRPCInterface(CobblerXMLRPCInterface):
Also if living in /etc/cobbler the file must be a kickstart file.
"""
- self.__validate_token(token)
+ self.log("read_or_write_kickstart_template",name=kickstart_file,token=token)
+ self.check_access(token,"read_or_write_kickstart_templates",kickstart_file,is_read)
if kickstart_file.find("..") != -1 or not kickstart_file.startswith("/"):
raise CX(_("tainted file location"))
@@ -792,8 +993,8 @@ class CobblerReadWriteXMLRPCInterface(CobblerXMLRPCInterface):
-# *********************************************************************************
-# *********************************************************************************
+# *********************************************************************
+# *********************************************************************
class CobblerReadWriteXMLRPCServer(SimpleXMLRPCServer.SimpleXMLRPCServer):
"""
@@ -804,76 +1005,3 @@ class CobblerReadWriteXMLRPCServer(SimpleXMLRPCServer.SimpleXMLRPCServer):
self.allow_reuse_address = True
SimpleXMLRPCServer.SimpleXMLRPCServer.__init__(self,args)
-# *********************************************************************************
-# *********************************************************************************
-
-if __name__ == "__main__":
-
- # note: this demo requires that
- # (A) /etc/cobbler/auth.conf has a "testuser/llamas2007" account
- # (B) xmlrpc_rw_enabled is turned on /var/lib/cobbler/settings
- # (C) cobblerd is running (and restarted if changing any of the above)
- # (D) apache is configured as a reverse proxy (see cobbler.conf in /etc/httpd/conf.d)
- # this demo does not use SSL yet -- it /should/ and /can/.
-
- my_uri = "http://127.0.0.1/cobbler_api_rw"
- remote = xmlrpclib.Server(my_uri)
-
- testuser = "admin"
- testpass = "mooses9"
-
- token = remote.login(testuser,testpass)
- print token
-
- # just to make things "work"
- os.system("touch /tmp/vmlinuz")
- os.system("touch /tmp/initrd.img")
- os.system("touch /tmp/fake.ks")
-
- # now add a distro
- distro_id = remote.new_distro(token)
- remote.modify_distro(distro_id, 'name', 'example-distro',token)
- remote.modify_distro(distro_id, 'kernel', '/tmp/vmlinuz',token)
- remote.modify_distro(distro_id, 'initrd', '/tmp/initrd.img',token)
- remote.save_distro(distro_id,token)
-
- # now add a repository (that's not really mirroring anything useful)
- repo_id = remote.new_repo(token)
- remote.modify_repo(repo_id, 'name', 'example-repo', token)
- remote.modify_repo(repo_id, 'mirror', 'rsync://mirror.example.org/foo', token)
- remote.save_repo(repo_id, token)
-
- # now add a profile
- profile_id = remote.new_profile(token)
- remote.modify_profile(profile_id, 'name', 'example-profile', token)
- remote.modify_profile(profile_id, 'distro', 'example-distro', token)
- remote.modify_profile(profile_id, 'kickstart', '/tmp/fake.ks', token)
- remote.modify_profile(profile_id, 'repos', ['example-repo'], token)
- remote.save_profile(profile_id, token)
-
- # now add a system
- system_id = remote.new_system(token)
- remote.modify_system(system_id, 'name', 'example-system', token)
- remote.modify_system(system_id, 'profile', 'example-profile', token)
- remote.save_system(system_id, token)
-
- print remote.get_distros()
- print remote.get_profiles()
- print remote.get_systems()
- print remote.get_repos()
-
- print remote.get_system("AA:BB:AA:BB:AA:BB",True) # flattened
-
- # now simulate hitting a "sync" button in a WebUI
- print remote.sync(token)
-
- # the following code just tests a failed connection:
- #remote = CobblerReadWriteXMLRPCInterface(api,logger)
- #try:
- # token = remote.login("exampleuser2","examplepass")
- #except:
- # token = "fake_token"
- #print token
- #rc = remote.test(token)
- #print "test result: %s" % rc
- # print "cache: %s" % remote.token_cache