1""" 2Spacewalk Runner 3================ 4 5.. versionadded:: 2016.3.0 6 7Runner to interact with Spacewalk using Spacewalk API 8 9:codeauthor: Nitin Madhok <nmadhok@g.clemson.edu>, Joachim Werner <joe@suse.com>, Benedikt Werner <1benediktwerner@gmail.com> 10:maintainer: Benedikt Werner <1benediktwerner@gmail.com> 11 12To use this runner, set up the Spacewalk URL, username and password in the 13master configuration at ``/etc/salt/master`` or ``/etc/salt/master.d/spacewalk.conf``: 14 15.. code-block:: yaml 16 17 spacewalk: 18 spacewalk01.domain.com: 19 username: 'testuser' 20 password: 'verybadpass' 21 spacewalk02.domain.com: 22 username: 'testuser' 23 password: 'verybadpass' 24 25.. note:: 26 27 Optionally, ``protocol`` can be specified if the spacewalk server is 28 not using the defaults. Default is ``protocol: https``. 29 30""" 31 32import atexit 33import logging 34import xmlrpc.client 35 36log = logging.getLogger(__name__) 37 38_sessions = {} 39 40 41def __virtual__(): 42 """ 43 Check for spacewalk configuration in master config file 44 or directory and load runner only if it is specified 45 """ 46 if not _get_spacewalk_configuration(): 47 return False, "No spacewalk configuration found" 48 return True 49 50 51def _get_spacewalk_configuration(spacewalk_url=""): 52 """ 53 Return the configuration read from the master configuration 54 file or directory 55 """ 56 spacewalk_config = __opts__["spacewalk"] if "spacewalk" in __opts__ else None 57 58 if spacewalk_config: 59 try: 60 for spacewalk_server, service_config in spacewalk_config.items(): 61 username = service_config.get("username", None) 62 password = service_config.get("password", None) 63 protocol = service_config.get("protocol", "https") 64 65 if not username or not password: 66 log.error( 67 "Username or Password has not been specified in the master " 68 "configuration for %s", 69 spacewalk_server, 70 ) 71 return False 72 73 ret = { 74 "api_url": "{}://{}/rpc/api".format(protocol, spacewalk_server), 75 "username": username, 76 "password": password, 77 } 78 79 if (not spacewalk_url) or (spacewalk_url == spacewalk_server): 80 return ret 81 except Exception as exc: # pylint: disable=broad-except 82 log.error("Exception encountered: %s", exc) 83 return False 84 85 if spacewalk_url: 86 log.error( 87 "Configuration for %s has not been specified in the master " 88 "configuration", 89 spacewalk_url, 90 ) 91 return False 92 93 return False 94 95 96def _get_client_and_key(url, user, password, verbose=0): 97 """ 98 Return the client object and session key for the client 99 """ 100 session = {} 101 session["client"] = xmlrpc.client.Server(url, verbose=verbose, use_datetime=True) 102 session["key"] = session["client"].auth.login(user, password) 103 104 return session 105 106 107def _disconnect_session(session): 108 """ 109 Disconnect API connection 110 """ 111 session["client"].auth.logout(session["key"]) 112 113 114def _get_session(server): 115 """ 116 Get session and key 117 """ 118 if server in _sessions: 119 return _sessions[server] 120 121 config = _get_spacewalk_configuration(server) 122 if not config: 123 raise Exception("No config for '{}' found on master".format(server)) 124 125 session = _get_client_and_key( 126 config["api_url"], config["username"], config["password"] 127 ) 128 atexit.register(_disconnect_session, session) 129 130 client = session["client"] 131 key = session["key"] 132 _sessions[server] = (client, key) 133 134 return client, key 135 136 137def api(server, command, *args, **kwargs): 138 """ 139 Call the Spacewalk xmlrpc api. 140 141 CLI Example: 142 143 .. code-block:: bash 144 145 salt-run spacewalk.api spacewalk01.domain.com systemgroup.create MyGroup Description 146 salt-run spacewalk.api spacewalk01.domain.com systemgroup.create arguments='["MyGroup", "Description"]' 147 148 State Example: 149 150 .. code-block:: yaml 151 152 create_group: 153 salt.runner: 154 - name: spacewalk.api 155 - server: spacewalk01.domain.com 156 - command: systemgroup.create 157 - arguments: 158 - MyGroup 159 - Description 160 """ 161 if "arguments" in kwargs: 162 arguments = kwargs["arguments"] 163 else: 164 arguments = args 165 166 call = "{} {}".format(command, arguments) 167 try: 168 client, key = _get_session(server) 169 except Exception as exc: # pylint: disable=broad-except 170 err_msg = ( 171 "Exception raised when connecting to spacewalk server ({}): {}".format( 172 server, exc 173 ) 174 ) 175 log.error(err_msg) 176 return {call: err_msg} 177 178 namespace, _, method = command.rpartition(".") 179 if not namespace: 180 return { 181 call: "Error: command must use the following format: 'namespace.method'" 182 } 183 endpoint = getattr(getattr(client, namespace), method) 184 185 try: 186 output = endpoint(key, *arguments) 187 except Exception as e: # pylint: disable=broad-except 188 output = "API call failed: {}".format(e) 189 190 return {call: output} 191 192 193def addGroupsToKey(server, activation_key, groups): 194 """ 195 Add server groups to a activation key 196 197 CLI Example: 198 199 .. code-block:: bash 200 201 salt-run spacewalk.addGroupsToKey spacewalk01.domain.com 1-my-key '[group1, group2]' 202 """ 203 204 try: 205 client, key = _get_session(server) 206 except Exception as exc: # pylint: disable=broad-except 207 err_msg = ( 208 "Exception raised when connecting to spacewalk server ({}): {}".format( 209 server, exc 210 ) 211 ) 212 log.error(err_msg) 213 return {"Error": err_msg} 214 215 all_groups = client.systemgroup.listAllGroups(key) 216 groupIds = [] 217 for group in all_groups: 218 if group["name"] in groups: 219 groupIds.append(group["id"]) 220 221 if client.activationkey.addServerGroups(key, activation_key, groupIds) == 1: 222 return {activation_key: groups} 223 else: 224 return {activation_key: "Failed to add groups to activation key"} 225 226 227def deleteAllGroups(server): 228 """ 229 Delete all server groups from Spacewalk 230 """ 231 232 try: 233 client, key = _get_session(server) 234 except Exception as exc: # pylint: disable=broad-except 235 err_msg = ( 236 "Exception raised when connecting to spacewalk server ({}): {}".format( 237 server, exc 238 ) 239 ) 240 log.error(err_msg) 241 return {"Error": err_msg} 242 243 groups = client.systemgroup.listAllGroups(key) 244 245 deleted_groups = [] 246 failed_groups = [] 247 248 for group in groups: 249 if client.systemgroup.delete(key, group["name"]) == 1: 250 deleted_groups.append(group["name"]) 251 else: 252 failed_groups.append(group["name"]) 253 254 ret = {"deleted": deleted_groups} 255 if failed_groups: 256 ret["failed"] = failed_groups 257 return ret 258 259 260def deleteAllSystems(server): 261 """ 262 Delete all systems from Spacewalk 263 264 CLI Example: 265 266 .. code-block:: bash 267 268 salt-run spacewalk.deleteAllSystems spacewalk01.domain.com 269 """ 270 271 try: 272 client, key = _get_session(server) 273 except Exception as exc: # pylint: disable=broad-except 274 err_msg = ( 275 "Exception raised when connecting to spacewalk server ({}): {}".format( 276 server, exc 277 ) 278 ) 279 log.error(err_msg) 280 return {"Error": err_msg} 281 282 systems = client.system.listSystems(key) 283 284 ids = [] 285 names = [] 286 for system in systems: 287 ids.append(system["id"]) 288 names.append(system["name"]) 289 290 if client.system.deleteSystems(key, ids) == 1: 291 return {"deleted": names} 292 else: 293 return {"Error": "Failed to delete all systems"} 294 295 296def deleteAllActivationKeys(server): 297 """ 298 Delete all activation keys from Spacewalk 299 300 CLI Example: 301 302 .. code-block:: bash 303 304 salt-run spacewalk.deleteAllActivationKeys spacewalk01.domain.com 305 """ 306 307 try: 308 client, key = _get_session(server) 309 except Exception as exc: # pylint: disable=broad-except 310 err_msg = ( 311 "Exception raised when connecting to spacewalk server ({}): {}".format( 312 server, exc 313 ) 314 ) 315 log.error(err_msg) 316 return {"Error": err_msg} 317 318 activation_keys = client.activationkey.listActivationKeys(key) 319 320 deleted_keys = [] 321 failed_keys = [] 322 323 for aKey in activation_keys: 324 if client.activationkey.delete(key, aKey["key"]) == 1: 325 deleted_keys.append(aKey["key"]) 326 else: 327 failed_keys.append(aKey["key"]) 328 329 ret = {"deleted": deleted_keys} 330 if failed_keys: 331 ret["failed"] = failed_keys 332 return ret 333 334 335def unregister(name, server_url): 336 """ 337 Unregister specified server from Spacewalk 338 339 CLI Example: 340 341 .. code-block:: bash 342 343 salt-run spacewalk.unregister my-test-vm spacewalk01.domain.com 344 """ 345 346 try: 347 client, key = _get_session(server_url) 348 except Exception as exc: # pylint: disable=broad-except 349 err_msg = ( 350 "Exception raised when connecting to spacewalk server ({}): {}".format( 351 server_url, exc 352 ) 353 ) 354 log.error(err_msg) 355 return {name: err_msg} 356 357 systems_list = client.system.getId(key, name) 358 359 if systems_list: 360 for system in systems_list: 361 out = client.system.deleteSystem(key, system["id"]) 362 if out == 1: 363 return {name: "Successfully unregistered from {}".format(server_url)} 364 else: 365 return {name: "Failed to unregister from {}".format(server_url)} 366 else: 367 return { 368 name: "System does not exist in spacewalk server ({})".format(server_url) 369 } 370