1""" 2Runner for SmartOS minions control vmadm 3""" 4 5import salt.client 6from salt.exceptions import SaltClientError 7from salt.utils.odict import OrderedDict 8 9# Function aliases 10__func_alias__ = {"list_vms": "list"} 11 12# Define the module's virtual name 13__virtualname__ = "vmadm" 14 15 16def __virtual__(): 17 """ 18 Provides vmadm runner 19 """ 20 # NOTE: always load vmadm runner 21 # we could check using test.ping + a grain 22 # match, but doing this on master startup is 23 # not acceptable 24 return __virtualname__ 25 26 27def _action(action="get", search=None, one=True, force=False): 28 """ 29 Multi action helper for start, stop, get, ... 30 """ 31 vms = {} 32 matched_vms = [] 33 34 ## lookup vms 35 with salt.client.get_local_client(__opts__["conf_file"]) as client: 36 try: 37 vmadm_args = {} 38 vmadm_args["order"] = "uuid,alias,hostname,state" 39 if "=" in search: 40 vmadm_args["search"] = search 41 for cn in client.cmd_iter( 42 "G@virtual:physical and G@os:smartos", 43 "vmadm.list", 44 kwarg=vmadm_args, 45 tgt_type="compound", 46 ): 47 if not cn: 48 continue 49 node = next(iter(cn.keys())) 50 if ( 51 not isinstance(cn[node], dict) 52 or "ret" not in cn[node] 53 or not isinstance(cn[node]["ret"], dict) 54 ): 55 continue 56 for vm in cn[node]["ret"]: 57 vmcfg = cn[node]["ret"][vm] 58 vmcfg["node"] = node 59 vms[vm] = vmcfg 60 except SaltClientError as client_error: 61 pass 62 63 ## check if we have vms 64 if len(vms) == 0: 65 return {"Error": "No vms found."} 66 67 ## simple search 68 if "=" not in search: 69 loop_pass = 0 70 while loop_pass < 3: 71 ## each pass will try a different field 72 if loop_pass == 0: 73 field = "uuid" 74 elif loop_pass == 1: 75 field = "hostname" 76 else: 77 field = "alias" 78 79 ## loop vms and try to match 80 for vm in vms: 81 if field == "uuid" and vm == search: 82 matched_vms.append(vm) 83 break # exit for on uuid match (max = 1) 84 elif field in vms[vm] and vms[vm][field] == search: 85 matched_vms.append(vm) 86 87 ## exit on match(es) or try again 88 if len(matched_vms) > 0: 89 break 90 else: 91 loop_pass += 1 92 else: 93 for vm in vms: 94 matched_vms.append(vm) 95 96 ## check if we have vms 97 if len(matched_vms) == 0: 98 return {"Error": "No vms matched."} 99 100 ## multiple allowed? 101 if one and len(matched_vms) > 1: 102 return { 103 "Error": "Matched {} vms, only one allowed!".format(len(matched_vms)), 104 "Matches": matched_vms, 105 } 106 107 ## perform action 108 ret = {} 109 if action in ["start", "stop", "reboot", "get"]: 110 for vm in matched_vms: 111 vmadm_args = {"key": "uuid", "vm": vm} 112 try: 113 for vmadm_res in client.cmd_iter( 114 vms[vm]["node"], "vmadm.{}".format(action), kwarg=vmadm_args 115 ): 116 if not vmadm_res: 117 continue 118 if vms[vm]["node"] in vmadm_res: 119 ret[vm] = vmadm_res[vms[vm]["node"]]["ret"] 120 except SaltClientError as client_error: 121 ret[vm] = False 122 elif action in ["is_running"]: 123 ret = True 124 for vm in matched_vms: 125 if vms[vm]["state"] != "running": 126 ret = False 127 break 128 return ret 129 130 131def nodes(verbose=False): 132 """ 133 List all compute nodes 134 135 verbose : boolean 136 print additional information about the node 137 e.g. platform version, hvm capable, ... 138 139 CLI Example: 140 141 .. code-block:: bash 142 143 salt-run vmadm.nodes 144 salt-run vmadm.nodes verbose=True 145 """ 146 ret = {} if verbose else [] 147 with salt.client.get_local_client(__opts__["conf_file"]) as client: 148 149 ## get list of nodes 150 try: 151 for cn in client.cmd_iter( 152 "G@virtual:physical and G@os:smartos", 153 "grains.items", 154 tgt_type="compound", 155 ): 156 if not cn: 157 continue 158 node = next(iter(cn.keys())) 159 if ( 160 not isinstance(cn[node], dict) 161 or "ret" not in cn[node] 162 or not isinstance(cn[node]["ret"], dict) 163 ): 164 continue 165 if verbose: 166 ret[node] = {} 167 ret[node]["version"] = {} 168 ret[node]["version"]["platform"] = cn[node]["ret"]["osrelease"] 169 if "computenode_sdc_version" in cn[node]["ret"]: 170 ret[node]["version"]["sdc"] = cn[node]["ret"][ 171 "computenode_sdc_version" 172 ] 173 ret[node]["vms"] = {} 174 if ( 175 "computenode_vm_capable" in cn[node]["ret"] 176 and cn[node]["ret"]["computenode_vm_capable"] 177 and "computenode_vm_hw_virt" in cn[node]["ret"] 178 ): 179 ret[node]["vms"]["hw_cap"] = cn[node]["ret"][ 180 "computenode_vm_hw_virt" 181 ] 182 else: 183 ret[node]["vms"]["hw_cap"] = False 184 if "computenode_vms_running" in cn[node]["ret"]: 185 ret[node]["vms"]["running"] = cn[node]["ret"][ 186 "computenode_vms_running" 187 ] 188 else: 189 ret.append(node) 190 except SaltClientError as client_error: 191 return "{}".format(client_error) 192 193 if not verbose: 194 ret.sort() 195 return ret 196 197 198def list_vms(search=None, verbose=False): 199 """ 200 List all vms 201 202 search : string 203 filter vms, see the execution module 204 verbose : boolean 205 print additional information about the vm 206 207 CLI Example: 208 209 .. code-block:: bash 210 211 salt-run vmadm.list 212 salt-run vmadm.list search='type=KVM' 213 salt-run vmadm.list verbose=True 214 """ 215 ret = OrderedDict() if verbose else [] 216 with salt.client.get_local_client(__opts__["conf_file"]) as client: 217 try: 218 vmadm_args = {} 219 vmadm_args["order"] = "uuid,alias,hostname,state,type,cpu_cap,vcpus,ram" 220 if search: 221 vmadm_args["search"] = search 222 for cn in client.cmd_iter( 223 "G@virtual:physical and G@os:smartos", 224 "vmadm.list", 225 kwarg=vmadm_args, 226 tgt_type="compound", 227 ): 228 if not cn: 229 continue 230 node = next(iter(cn.keys())) 231 if ( 232 not isinstance(cn[node], dict) 233 or "ret" not in cn[node] 234 or not isinstance(cn[node]["ret"], dict) 235 ): 236 continue 237 for vm in cn[node]["ret"]: 238 vmcfg = cn[node]["ret"][vm] 239 if verbose: 240 ret[vm] = OrderedDict() 241 ret[vm]["hostname"] = vmcfg["hostname"] 242 ret[vm]["alias"] = vmcfg["alias"] 243 ret[vm]["computenode"] = node 244 ret[vm]["state"] = vmcfg["state"] 245 ret[vm]["resources"] = OrderedDict() 246 ret[vm]["resources"]["memory"] = vmcfg["ram"] 247 if vmcfg["type"] == "KVM": 248 ret[vm]["resources"]["cpu"] = "{:.2f}".format( 249 int(vmcfg["vcpus"]) 250 ) 251 else: 252 if vmcfg["cpu_cap"] != "": 253 ret[vm]["resources"]["cpu"] = "{:.2f}".format( 254 int(vmcfg["cpu_cap"]) / 100 255 ) 256 else: 257 ret.append(vm) 258 except SaltClientError as client_error: 259 return "{}".format(client_error) 260 261 if not verbose: 262 ret = sorted(ret) 263 264 return ret 265 266 267def start(search, one=True): 268 """ 269 Start one or more vms 270 271 search : string 272 filter vms, see the execution module. 273 one : boolean 274 start only one vm 275 276 .. note:: 277 If the search parameter does not contain an equal (=) symbol it will be 278 assumed it will be tried as uuid, hostname, and alias. 279 280 CLI Example: 281 282 .. code-block:: bash 283 284 salt-run vmadm.start 91244bba-1146-e4ec-c07e-e825e0223aa9 285 salt-run vmadm.start search='alias=jiska' 286 salt-run vmadm.start search='type=KVM' one=False 287 """ 288 return _action("start", search, one) 289 290 291def stop(search, one=True): 292 """ 293 Stop one or more vms 294 295 search : string 296 filter vms, see the execution module. 297 one : boolean 298 stop only one vm 299 300 .. note:: 301 If the search parameter does not contain an equal (=) symbol it will be 302 assumed it will be tried as uuid, hostname, and alias. 303 304 CLI Example: 305 306 .. code-block:: bash 307 308 salt-run vmadm.stop 91244bba-1146-e4ec-c07e-e825e0223aa9 309 salt-run vmadm.stop search='alias=jody' 310 salt-run vmadm.stop search='type=KVM' one=False 311 """ 312 return _action("stop", search, one) 313 314 315def reboot(search, one=True, force=False): 316 """ 317 Reboot one or more vms 318 319 search : string 320 filter vms, see the execution module. 321 one : boolean 322 reboot only one vm 323 force : boolean 324 force reboot, faster but no graceful shutdown 325 326 .. note:: 327 If the search parameter does not contain an equal (=) symbol it will be 328 assumed it will be tried as uuid, hostname, and alias. 329 330 CLI Example: 331 332 .. code-block:: bash 333 334 salt-run vmadm.reboot 91244bba-1146-e4ec-c07e-e825e0223aa9 335 salt-run vmadm.reboot search='alias=marije' 336 salt-run vmadm.reboot search='type=KVM' one=False 337 """ 338 return _action("reboot", search, one, force) 339 340 341def get(search, one=True): 342 """ 343 Return information for vms 344 345 search : string 346 filter vms, see the execution module. 347 one : boolean 348 return only one vm 349 350 .. note:: 351 If the search parameter does not contain an equal (=) symbol it will be 352 assumed it will be tried as uuid, hostname, and alias. 353 354 CLI Example: 355 356 .. code-block:: bash 357 358 salt-run vmadm.get 91244bba-1146-e4ec-c07e-e825e0223aa9 359 salt-run vmadm.get search='alias=saskia' 360 """ 361 return _action("get", search, one) 362 363 364def is_running(search): 365 """ 366 Return true if vm is running 367 368 search : string 369 filter vms, see the execution module. 370 371 .. note:: 372 If the search parameter does not contain an equal (=) symbol it will be 373 assumed it will be tried as uuid, hostname, and alias. 374 375 .. note:: 376 If multiple vms are matched, the result will be true of ALL vms are running 377 378 CLI Example: 379 380 .. code-block:: bash 381 382 salt-run vmadm.is_running 91244bba-1146-e4ec-c07e-e825e0223aa9 383 salt-run vmadm.is_running search='alias=julia' 384 """ 385 return _action("is_running", search, False) 386 387 388# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 389