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