1"""
2Manage Dell DRAC.
3
4.. versionadded:: 2015.8.2
5"""
6
7
8import logging
9import os
10import re
11
12import salt.utils.path
13from salt.exceptions import CommandExecutionError
14
15log = logging.getLogger(__name__)
16
17__proxyenabled__ = ["fx2"]
18
19try:
20    run_all = __salt__["cmd.run_all"]
21except (NameError, KeyError):
22    import salt.modules.cmdmod
23
24    __salt__ = {"cmd.run_all": salt.modules.cmdmod.run_all}
25
26
27def __virtual__():
28    if salt.utils.path.which("racadm"):
29        return True
30
31    return (
32        False,
33        "The drac execution module cannot be loaded: racadm binary not in path.",
34    )
35
36
37def __parse_drac(output):
38    """
39    Parse Dell DRAC output
40    """
41    drac = {}
42    section = ""
43
44    for i in output.splitlines():
45        if i.strip().endswith(":") and "=" not in i:
46            section = i[0:-1]
47            drac[section] = {}
48        if i.rstrip() and "=" in i:
49            if section in drac:
50                drac[section].update(dict([[prop.strip() for prop in i.split("=")]]))
51            else:
52                section = i.strip()
53                if section not in drac and section:
54                    drac[section] = {}
55
56    return drac
57
58
59def __execute_cmd(
60    command, host=None, admin_username=None, admin_password=None, module=None
61):
62    """
63    Execute rac commands
64    """
65    if module:
66        # -a takes 'server' or 'switch' to represent all servers
67        # or all switches in a chassis.  Allow
68        # user to say 'module=ALL_SERVER' or 'module=ALL_SWITCH'
69        if module.startswith("ALL_"):
70            modswitch = "-a " + module[module.index("_") + 1 : len(module)].lower()
71        else:
72            modswitch = "-m {}".format(module)
73    else:
74        modswitch = ""
75    if not host:
76        # This is a local call
77        cmd = __salt__["cmd.run_all"]("racadm {} {}".format(command, modswitch))
78    else:
79        cmd = __salt__["cmd.run_all"](
80            "racadm -r {} -u {} -p {} {} {}".format(
81                host, admin_username, admin_password, command, modswitch
82            ),
83            output_loglevel="quiet",
84        )
85
86    if cmd["retcode"] != 0:
87        log.warning("racadm returned an exit code of %s", cmd["retcode"])
88        return False
89
90    return True
91
92
93def __execute_ret(
94    command, host=None, admin_username=None, admin_password=None, module=None
95):
96    """
97    Execute rac commands
98    """
99    if module:
100        if module == "ALL":
101            modswitch = "-a "
102        else:
103            modswitch = "-m {}".format(module)
104    else:
105        modswitch = ""
106    if not host:
107        # This is a local call
108        cmd = __salt__["cmd.run_all"]("racadm {} {}".format(command, modswitch))
109    else:
110        cmd = __salt__["cmd.run_all"](
111            "racadm -r {} -u {} -p {} {} {}".format(
112                host, admin_username, admin_password, command, modswitch
113            ),
114            output_loglevel="quiet",
115        )
116
117    if cmd["retcode"] != 0:
118        log.warning("racadm returned an exit code of %s", cmd["retcode"])
119    else:
120        fmtlines = []
121        for l in cmd["stdout"].splitlines():
122            if l.startswith("Security Alert"):
123                continue
124            if l.startswith("RAC1168:"):
125                break
126            if l.startswith("RAC1169:"):
127                break
128            if l.startswith("Continuing execution"):
129                continue
130
131            if not l.strip():
132                continue
133            fmtlines.append(l)
134            if "=" in l:
135                continue
136        cmd["stdout"] = "\n".join(fmtlines)
137
138    return cmd
139
140
141def get_dns_dracname(host=None, admin_username=None, admin_password=None):
142
143    ret = __execute_ret(
144        "get iDRAC.NIC.DNSRacName",
145        host=host,
146        admin_username=admin_username,
147        admin_password=admin_password,
148    )
149    parsed = __parse_drac(ret["stdout"])
150    return parsed
151
152
153def set_dns_dracname(name, host=None, admin_username=None, admin_password=None):
154
155    ret = __execute_ret(
156        "set iDRAC.NIC.DNSRacName {}".format(name),
157        host=host,
158        admin_username=admin_username,
159        admin_password=admin_password,
160    )
161    return ret
162
163
164def system_info(host=None, admin_username=None, admin_password=None, module=None):
165    """
166    Return System information
167
168    CLI Example:
169
170    .. code-block:: bash
171
172        salt dell dracr.system_info
173    """
174    cmd = __execute_ret(
175        "getsysinfo",
176        host=host,
177        admin_username=admin_username,
178        admin_password=admin_password,
179        module=module,
180    )
181
182    if cmd["retcode"] != 0:
183        log.warning("racadm returned an exit code of %s", cmd["retcode"])
184        return cmd
185
186    return __parse_drac(cmd["stdout"])
187
188
189def set_niccfg(
190    ip=None,
191    netmask=None,
192    gateway=None,
193    dhcp=False,
194    host=None,
195    admin_username=None,
196    admin_password=None,
197    module=None,
198):
199
200    cmdstr = "setniccfg "
201
202    if dhcp:
203        cmdstr += "-d "
204    else:
205        cmdstr += "-s " + ip + " " + netmask + " " + gateway
206
207    return __execute_cmd(
208        cmdstr,
209        host=host,
210        admin_username=admin_username,
211        admin_password=admin_password,
212        module=module,
213    )
214
215
216def set_nicvlan(
217    vlan=None, host=None, admin_username=None, admin_password=None, module=None
218):
219
220    cmdstr = "setniccfg -v "
221
222    if vlan:
223        cmdstr += vlan
224
225    ret = __execute_cmd(
226        cmdstr,
227        host=host,
228        admin_username=admin_username,
229        admin_password=admin_password,
230        module=module,
231    )
232
233    return ret
234
235
236def network_info(host=None, admin_username=None, admin_password=None, module=None):
237    """
238    Return Network Configuration
239
240    CLI Example:
241
242    .. code-block:: bash
243
244        salt dell dracr.network_info
245    """
246
247    inv = inventory(
248        host=host, admin_username=admin_username, admin_password=admin_password
249    )
250    if inv is None:
251        cmd = {}
252        cmd["retcode"] = -1
253        cmd["stdout"] = "Problem getting switch inventory"
254        return cmd
255
256    if module not in inv.get("switch") and module not in inv.get("server"):
257        cmd = {}
258        cmd["retcode"] = -1
259        cmd["stdout"] = "No module {} found.".format(module)
260        return cmd
261
262    cmd = __execute_ret(
263        "getniccfg",
264        host=host,
265        admin_username=admin_username,
266        admin_password=admin_password,
267        module=module,
268    )
269
270    if cmd["retcode"] != 0:
271        log.warning("racadm returned an exit code of %s", cmd["retcode"])
272
273    cmd["stdout"] = "Network:\n" + "Device = " + module + "\n" + cmd["stdout"]
274    return __parse_drac(cmd["stdout"])
275
276
277def nameservers(ns, host=None, admin_username=None, admin_password=None, module=None):
278    """
279    Configure the nameservers on the DRAC
280
281    CLI Example:
282
283    .. code-block:: bash
284
285        salt dell dracr.nameservers [NAMESERVERS]
286        salt dell dracr.nameservers ns1.example.com ns2.example.com
287            admin_username=root admin_password=calvin module=server-1
288            host=192.168.1.1
289    """
290    if len(ns) > 2:
291        log.warning("racadm only supports two nameservers")
292        return False
293
294    for i in range(1, len(ns) + 1):
295        if not __execute_cmd(
296            "config -g cfgLanNetworking -o cfgDNSServer{} {}".format(i, ns[i - 1]),
297            host=host,
298            admin_username=admin_username,
299            admin_password=admin_password,
300            module=module,
301        ):
302            return False
303
304    return True
305
306
307def syslog(
308    server,
309    enable=True,
310    host=None,
311    admin_username=None,
312    admin_password=None,
313    module=None,
314):
315    """
316    Configure syslog remote logging, by default syslog will automatically be
317    enabled if a server is specified. However, if you want to disable syslog
318    you will need to specify a server followed by False
319
320    CLI Example:
321
322    .. code-block:: bash
323
324        salt dell dracr.syslog [SYSLOG IP] [ENABLE/DISABLE]
325        salt dell dracr.syslog 0.0.0.0 False
326    """
327    if enable and __execute_cmd(
328        "config -g cfgRemoteHosts -o cfgRhostsSyslogEnable 1",
329        host=host,
330        admin_username=admin_username,
331        admin_password=admin_password,
332        module=None,
333    ):
334        return __execute_cmd(
335            "config -g cfgRemoteHosts -o cfgRhostsSyslogServer1 {}".format(server),
336            host=host,
337            admin_username=admin_username,
338            admin_password=admin_password,
339            module=module,
340        )
341
342    return __execute_cmd(
343        "config -g cfgRemoteHosts -o cfgRhostsSyslogEnable 0",
344        host=host,
345        admin_username=admin_username,
346        admin_password=admin_password,
347        module=module,
348    )
349
350
351def email_alerts(action, host=None, admin_username=None, admin_password=None):
352    """
353    Enable/Disable email alerts
354
355    CLI Example:
356
357    .. code-block:: bash
358
359        salt dell dracr.email_alerts True
360        salt dell dracr.email_alerts False
361    """
362
363    if action:
364        return __execute_cmd(
365            "config -g cfgEmailAlert -o cfgEmailAlertEnable -i 1 1",
366            host=host,
367            admin_username=admin_username,
368            admin_password=admin_password,
369        )
370    else:
371        return __execute_cmd("config -g cfgEmailAlert -o cfgEmailAlertEnable -i 1 0")
372
373
374def list_users(host=None, admin_username=None, admin_password=None, module=None):
375    """
376    List all DRAC users
377
378    CLI Example:
379
380    .. code-block:: bash
381
382        salt dell dracr.list_users
383    """
384    users = {}
385    _username = ""
386
387    for idx in range(1, 17):
388        cmd = __execute_ret(
389            "getconfig -g cfgUserAdmin -i {}".format(idx),
390            host=host,
391            admin_username=admin_username,
392            admin_password=admin_password,
393        )
394
395        if cmd["retcode"] != 0:
396            log.warning("racadm returned an exit code of %s", cmd["retcode"])
397
398        for user in cmd["stdout"].splitlines():
399            if not user.startswith("cfg"):
400                continue
401
402            (key, val) = user.split("=")
403
404            if key.startswith("cfgUserAdminUserName"):
405                _username = val.strip()
406
407                if val:
408                    users[_username] = {"index": idx}
409                else:
410                    break
411            else:
412                if _username:
413                    users[_username].update({key: val})
414
415    return users
416
417
418def delete_user(
419    username, uid=None, host=None, admin_username=None, admin_password=None
420):
421    """
422    Delete a user
423
424    CLI Example:
425
426    .. code-block:: bash
427
428        salt dell dracr.delete_user [USERNAME] [UID - optional]
429        salt dell dracr.delete_user diana 4
430    """
431    if uid is None:
432        user = list_users()
433        uid = user[username]["index"]
434
435    if uid:
436        return __execute_cmd(
437            "config -g cfgUserAdmin -o cfgUserAdminUserName -i {} ".format(uid),
438            host=host,
439            admin_username=admin_username,
440            admin_password=admin_password,
441        )
442
443    else:
444        log.warning("User '%s' does not exist", username)
445        return False
446
447
448def change_password(
449    username,
450    password,
451    uid=None,
452    host=None,
453    admin_username=None,
454    admin_password=None,
455    module=None,
456):
457    """
458    Change user's password
459
460    CLI Example:
461
462    .. code-block:: bash
463
464        salt dell dracr.change_password [USERNAME] [PASSWORD] uid=[OPTIONAL]
465            host=<remote DRAC> admin_username=<DRAC user>
466            admin_password=<DRAC PW>
467        salt dell dracr.change_password diana secret
468
469    Note that if only a username is specified then this module will look up
470    details for all 16 possible DRAC users.  This is time consuming, but might
471    be necessary if one is not sure which user slot contains the one you want.
472    Many late-model Dell chassis have 'root' as UID 1, so if you can depend
473    on that then setting the password is much quicker.
474    Raises an error if the supplied password is greater than 20 chars.
475    """
476    if len(password) > 20:
477        raise CommandExecutionError("Supplied password should be 20 characters or less")
478
479    if uid is None:
480        user = list_users(
481            host=host,
482            admin_username=admin_username,
483            admin_password=admin_password,
484            module=module,
485        )
486        uid = user[username]["index"]
487
488    if uid:
489        return __execute_cmd(
490            "config -g cfgUserAdmin -o cfgUserAdminPassword -i {} {}".format(
491                uid, password
492            ),
493            host=host,
494            admin_username=admin_username,
495            admin_password=admin_password,
496            module=module,
497        )
498    else:
499        log.warning("racadm: user '%s' does not exist", username)
500        return False
501
502
503def deploy_password(
504    username, password, host=None, admin_username=None, admin_password=None, module=None
505):
506    """
507    Change the QuickDeploy password, used for switches as well
508
509    CLI Example:
510
511    .. code-block:: bash
512
513        salt dell dracr.deploy_password [USERNAME] [PASSWORD]
514            host=<remote DRAC> admin_username=<DRAC user>
515            admin_password=<DRAC PW>
516        salt dell dracr.change_password diana secret
517
518    Note that if only a username is specified then this module will look up
519    details for all 16 possible DRAC users.  This is time consuming, but might
520    be necessary if one is not sure which user slot contains the one you want.
521    Many late-model Dell chassis have 'root' as UID 1, so if you can depend
522    on that then setting the password is much quicker.
523    """
524    return __execute_cmd(
525        "deploy -u {} -p {}".format(username, password),
526        host=host,
527        admin_username=admin_username,
528        admin_password=admin_password,
529        module=module,
530    )
531
532
533def deploy_snmp(snmp, host=None, admin_username=None, admin_password=None, module=None):
534    """
535    Change the QuickDeploy SNMP community string, used for switches as well
536
537    CLI Example:
538
539    .. code-block:: bash
540
541        salt dell dracr.deploy_snmp SNMP_STRING
542            host=<remote DRAC or CMC> admin_username=<DRAC user>
543            admin_password=<DRAC PW>
544        salt dell dracr.deploy_password diana secret
545
546    """
547    return __execute_cmd(
548        "deploy -v SNMPv2 {} ro".format(snmp),
549        host=host,
550        admin_username=admin_username,
551        admin_password=admin_password,
552        module=module,
553    )
554
555
556def create_user(
557    username,
558    password,
559    permissions,
560    users=None,
561    host=None,
562    admin_username=None,
563    admin_password=None,
564):
565    """
566    Create user accounts
567
568    CLI Example:
569
570    .. code-block:: bash
571
572        salt dell dracr.create_user [USERNAME] [PASSWORD] [PRIVILEGES]
573        salt dell dracr.create_user diana secret login,test_alerts,clear_logs
574
575    DRAC Privileges
576      * login                   : Login to iDRAC
577      * drac                    : Configure iDRAC
578      * user_management         : Configure Users
579      * clear_logs              : Clear Logs
580      * server_control_commands : Execute Server Control Commands
581      * console_redirection     : Access Console Redirection
582      * virtual_media           : Access Virtual Media
583      * test_alerts             : Test Alerts
584      * debug_commands          : Execute Debug Commands
585    """
586    _uids = set()
587
588    if users is None:
589        users = list_users()
590
591    if username in users:
592        log.warning("racadm: user '%s' already exists", username)
593        return False
594
595    for idx in users.keys():
596        _uids.add(users[idx]["index"])
597
598    uid = sorted(list(set(range(2, 12)) - _uids), reverse=True).pop()
599
600    # Create user account first
601    if not __execute_cmd(
602        "config -g cfgUserAdmin -o cfgUserAdminUserName -i {} {}".format(uid, username),
603        host=host,
604        admin_username=admin_username,
605        admin_password=admin_password,
606    ):
607        delete_user(username, uid)
608        return False
609
610    # Configure users permissions
611    if not set_permissions(username, permissions, uid):
612        log.warning("unable to set user permissions")
613        delete_user(username, uid)
614        return False
615
616    # Configure users password
617    if not change_password(username, password, uid):
618        log.warning("unable to set user password")
619        delete_user(username, uid)
620        return False
621
622    # Enable users admin
623    if not __execute_cmd(
624        "config -g cfgUserAdmin -o cfgUserAdminEnable -i {} 1".format(uid)
625    ):
626        delete_user(username, uid)
627        return False
628
629    return True
630
631
632def set_permissions(
633    username, permissions, uid=None, host=None, admin_username=None, admin_password=None
634):
635    """
636    Configure users permissions
637
638    CLI Example:
639
640    .. code-block:: bash
641
642        salt dell dracr.set_permissions [USERNAME] [PRIVILEGES]
643             [USER INDEX - optional]
644        salt dell dracr.set_permissions diana login,test_alerts,clear_logs 4
645
646    DRAC Privileges
647      * login                   : Login to iDRAC
648      * drac                    : Configure iDRAC
649      * user_management         : Configure Users
650      * clear_logs              : Clear Logs
651      * server_control_commands : Execute Server Control Commands
652      * console_redirection     : Access Console Redirection
653      * virtual_media           : Access Virtual Media
654      * test_alerts             : Test Alerts
655      * debug_commands          : Execute Debug Commands
656    """
657    privileges = {
658        "login": "0x0000001",
659        "drac": "0x0000002",
660        "user_management": "0x0000004",
661        "clear_logs": "0x0000008",
662        "server_control_commands": "0x0000010",
663        "console_redirection": "0x0000020",
664        "virtual_media": "0x0000040",
665        "test_alerts": "0x0000080",
666        "debug_commands": "0x0000100",
667    }
668
669    permission = 0
670
671    # When users don't provide a user ID we need to search for this
672    if uid is None:
673        user = list_users()
674        uid = user[username]["index"]
675
676    # Generate privilege bit mask
677    for i in permissions.split(","):
678        perm = i.strip()
679
680        if perm in privileges:
681            permission += int(privileges[perm], 16)
682
683    return __execute_cmd(
684        "config -g cfgUserAdmin -o cfgUserAdminPrivilege -i {} 0x{:08X}".format(
685            uid, permission
686        ),
687        host=host,
688        admin_username=admin_username,
689        admin_password=admin_password,
690    )
691
692
693def set_snmp(community, host=None, admin_username=None, admin_password=None):
694    """
695    Configure CMC or individual iDRAC SNMP community string.
696    Use ``deploy_snmp`` for configuring chassis switch SNMP.
697
698    CLI Example:
699
700    .. code-block:: bash
701
702        salt dell dracr.set_snmp [COMMUNITY]
703        salt dell dracr.set_snmp public
704    """
705    return __execute_cmd(
706        "config -g cfgOobSnmp -o cfgOobSnmpAgentCommunity {}".format(community),
707        host=host,
708        admin_username=admin_username,
709        admin_password=admin_password,
710    )
711
712
713def set_network(
714    ip, netmask, gateway, host=None, admin_username=None, admin_password=None
715):
716    """
717    Configure Network on the CMC or individual iDRAC.
718    Use ``set_niccfg`` for blade and switch addresses.
719
720    CLI Example:
721
722    .. code-block:: bash
723
724        salt dell dracr.set_network [DRAC IP] [NETMASK] [GATEWAY]
725        salt dell dracr.set_network 192.168.0.2 255.255.255.0 192.168.0.1
726            admin_username=root admin_password=calvin host=192.168.1.1
727    """
728    return __execute_cmd(
729        "setniccfg -s {} {} {}".format(
730            ip,
731            netmask,
732            gateway,
733            host=host,
734            admin_username=admin_username,
735            admin_password=admin_password,
736        )
737    )
738
739
740def server_power(
741    status, host=None, admin_username=None, admin_password=None, module=None
742):
743    """
744    status
745        One of 'powerup', 'powerdown', 'powercycle', 'hardreset',
746        'graceshutdown'
747
748    host
749        The chassis host.
750
751    admin_username
752        The username used to access the chassis.
753
754    admin_password
755        The password used to access the chassis.
756
757    module
758        The element to reboot on the chassis such as a blade. If not provided,
759        the chassis will be rebooted.
760
761    CLI Example:
762
763    .. code-block:: bash
764
765        salt dell dracr.server_reboot
766        salt dell dracr.server_reboot module=server-1
767
768    """
769    return __execute_cmd(
770        "serveraction {}".format(status),
771        host=host,
772        admin_username=admin_username,
773        admin_password=admin_password,
774        module=module,
775    )
776
777
778def server_reboot(host=None, admin_username=None, admin_password=None, module=None):
779    """
780    Issues a power-cycle operation on the managed server. This action is
781    similar to pressing the power button on the system's front panel to
782    power down and then power up the system.
783
784    host
785        The chassis host.
786
787    admin_username
788        The username used to access the chassis.
789
790    admin_password
791        The password used to access the chassis.
792
793    module
794        The element to reboot on the chassis such as a blade. If not provided,
795        the chassis will be rebooted.
796
797    CLI Example:
798
799    .. code-block:: bash
800
801        salt dell dracr.server_reboot
802        salt dell dracr.server_reboot module=server-1
803
804    """
805    return __execute_cmd(
806        "serveraction powercycle",
807        host=host,
808        admin_username=admin_username,
809        admin_password=admin_password,
810        module=module,
811    )
812
813
814def server_poweroff(host=None, admin_username=None, admin_password=None, module=None):
815    """
816    Powers down the managed server.
817
818    host
819        The chassis host.
820
821    admin_username
822        The username used to access the chassis.
823
824    admin_password
825        The password used to access the chassis.
826
827    module
828        The element to power off on the chassis such as a blade.
829        If not provided, the chassis will be powered off.
830
831    CLI Example:
832
833    .. code-block:: bash
834
835        salt dell dracr.server_poweroff
836        salt dell dracr.server_poweroff module=server-1
837    """
838    return __execute_cmd(
839        "serveraction powerdown",
840        host=host,
841        admin_username=admin_username,
842        admin_password=admin_password,
843        module=module,
844    )
845
846
847def server_poweron(host=None, admin_username=None, admin_password=None, module=None):
848    """
849    Powers up the managed server.
850
851    host
852        The chassis host.
853
854    admin_username
855        The username used to access the chassis.
856
857    admin_password
858        The password used to access the chassis.
859
860    module
861        The element to power on located on the chassis such as a blade. If
862        not provided, the chassis will be powered on.
863
864    CLI Example:
865
866    .. code-block:: bash
867
868        salt dell dracr.server_poweron
869        salt dell dracr.server_poweron module=server-1
870    """
871    return __execute_cmd(
872        "serveraction powerup",
873        host=host,
874        admin_username=admin_username,
875        admin_password=admin_password,
876        module=module,
877    )
878
879
880def server_hardreset(host=None, admin_username=None, admin_password=None, module=None):
881    """
882    Performs a reset (reboot) operation on the managed server.
883
884    host
885        The chassis host.
886
887    admin_username
888        The username used to access the chassis.
889
890    admin_password
891        The password used to access the chassis.
892
893    module
894        The element to hard reset on the chassis such as a blade. If
895        not provided, the chassis will be reset.
896
897    CLI Example:
898
899    .. code-block:: bash
900
901        salt dell dracr.server_hardreset
902        salt dell dracr.server_hardreset module=server-1
903    """
904    return __execute_cmd(
905        "serveraction hardreset",
906        host=host,
907        admin_username=admin_username,
908        admin_password=admin_password,
909        module=module,
910    )
911
912
913def server_powerstatus(
914    host=None, admin_username=None, admin_password=None, module=None
915):
916    """
917    return the power status for the passed module
918
919    CLI Example:
920
921    .. code-block:: bash
922
923        salt dell drac.server_powerstatus
924    """
925    ret = __execute_ret(
926        "serveraction powerstatus",
927        host=host,
928        admin_username=admin_username,
929        admin_password=admin_password,
930        module=module,
931    )
932
933    result = {"retcode": 0}
934    if ret["stdout"] == "ON":
935        result["status"] = True
936        result["comment"] = "Power is on"
937    if ret["stdout"] == "OFF":
938        result["status"] = False
939        result["comment"] = "Power is on"
940    if ret["stdout"].startswith("ERROR"):
941        result["status"] = False
942        result["comment"] = ret["stdout"]
943
944    return result
945
946
947def server_pxe(host=None, admin_username=None, admin_password=None):
948    """
949    Configure server to PXE perform a one off PXE boot
950
951    CLI Example:
952
953    .. code-block:: bash
954
955        salt dell dracr.server_pxe
956    """
957    if __execute_cmd(
958        "config -g cfgServerInfo -o cfgServerFirstBootDevice PXE",
959        host=host,
960        admin_username=admin_username,
961        admin_password=admin_password,
962    ):
963        if __execute_cmd(
964            "config -g cfgServerInfo -o cfgServerBootOnce 1",
965            host=host,
966            admin_username=admin_username,
967            admin_password=admin_password,
968        ):
969            return server_reboot
970        else:
971            log.warning("failed to set boot order")
972            return False
973
974    log.warning("failed to configure PXE boot")
975    return False
976
977
978def list_slotnames(host=None, admin_username=None, admin_password=None):
979    """
980    List the names of all slots in the chassis.
981
982    host
983        The chassis host.
984
985    admin_username
986        The username used to access the chassis.
987
988    admin_password
989        The password used to access the chassis.
990
991    CLI Example:
992
993    .. code-block:: bash
994
995        salt-call --local dracr.list_slotnames host=111.222.333.444
996            admin_username=root admin_password=secret
997
998    """
999    slotraw = __execute_ret(
1000        "getslotname",
1001        host=host,
1002        admin_username=admin_username,
1003        admin_password=admin_password,
1004    )
1005
1006    if slotraw["retcode"] != 0:
1007        return slotraw
1008    slots = {}
1009    stripheader = True
1010    for l in slotraw["stdout"].splitlines():
1011        if l.startswith("<"):
1012            stripheader = False
1013            continue
1014        if stripheader:
1015            continue
1016        fields = l.split()
1017        slots[fields[0]] = {}
1018        slots[fields[0]]["slot"] = fields[0]
1019        if len(fields) > 1:
1020            slots[fields[0]]["slotname"] = fields[1]
1021        else:
1022            slots[fields[0]]["slotname"] = ""
1023        if len(fields) > 2:
1024            slots[fields[0]]["hostname"] = fields[2]
1025        else:
1026            slots[fields[0]]["hostname"] = ""
1027
1028    return slots
1029
1030
1031def get_slotname(slot, host=None, admin_username=None, admin_password=None):
1032    """
1033    Get the name of a slot number in the chassis.
1034
1035    slot
1036        The number of the slot for which to obtain the name.
1037
1038    host
1039        The chassis host.
1040
1041    admin_username
1042        The username used to access the chassis.
1043
1044    admin_password
1045        The password used to access the chassis.
1046
1047    CLI Example:
1048
1049    .. code-block:: bash
1050
1051        salt-call --local dracr.get_slotname 0 host=111.222.333.444
1052           admin_username=root admin_password=secret
1053
1054    """
1055    slots = list_slotnames(
1056        host=host, admin_username=admin_username, admin_password=admin_password
1057    )
1058    # The keys for this dictionary are strings, not integers, so convert the
1059    # argument to a string
1060    slot = str(slot)
1061    return slots[slot]["slotname"]
1062
1063
1064def set_slotname(slot, name, host=None, admin_username=None, admin_password=None):
1065    """
1066    Set the name of a slot in a chassis.
1067
1068    slot
1069        The slot number to change.
1070
1071    name
1072        The name to set. Can only be 15 characters long.
1073
1074    host
1075        The chassis host.
1076
1077    admin_username
1078        The username used to access the chassis.
1079
1080    admin_password
1081        The password used to access the chassis.
1082
1083    CLI Example:
1084
1085    .. code-block:: bash
1086
1087        salt '*' dracr.set_slotname 2 my-slotname host=111.222.333.444
1088            admin_username=root admin_password=secret
1089
1090    """
1091    return __execute_cmd(
1092        "config -g cfgServerInfo -o cfgServerName -i {} {}".format(slot, name),
1093        host=host,
1094        admin_username=admin_username,
1095        admin_password=admin_password,
1096    )
1097
1098
1099def set_chassis_name(name, host=None, admin_username=None, admin_password=None):
1100    """
1101    Set the name of the chassis.
1102
1103    name
1104        The name to be set on the chassis.
1105
1106    host
1107        The chassis host.
1108
1109    admin_username
1110        The username used to access the chassis.
1111
1112    admin_password
1113        The password used to access the chassis.
1114
1115    CLI Example:
1116
1117    .. code-block:: bash
1118
1119        salt '*' dracr.set_chassis_name my-chassis host=111.222.333.444
1120            admin_username=root admin_password=secret
1121
1122    """
1123    return __execute_cmd(
1124        "setsysinfo -c chassisname {}".format(name),
1125        host=host,
1126        admin_username=admin_username,
1127        admin_password=admin_password,
1128    )
1129
1130
1131def get_chassis_name(host=None, admin_username=None, admin_password=None):
1132    """
1133    Get the name of a chassis.
1134
1135    host
1136        The chassis host.
1137
1138    admin_username
1139        The username used to access the chassis.
1140
1141    admin_password
1142        The password used to access the chassis.
1143
1144    CLI Example:
1145
1146    .. code-block:: bash
1147
1148        salt '*' dracr.get_chassis_name host=111.222.333.444
1149            admin_username=root admin_password=secret
1150
1151    """
1152    return bare_rac_cmd(
1153        "getchassisname",
1154        host=host,
1155        admin_username=admin_username,
1156        admin_password=admin_password,
1157    )
1158
1159
1160def inventory(host=None, admin_username=None, admin_password=None):
1161    def mapit(x, y):
1162        return {x: y}
1163
1164    fields = {}
1165    fields["server"] = ["name", "idrac_version", "blade_type", "gen", "updateable"]
1166    fields["switch"] = ["name", "model_name", "hw_version", "fw_version"]
1167    fields["cmc"] = ["name", "cmc_version", "updateable"]
1168    fields["chassis"] = ["name", "fw_version", "fqdd"]
1169
1170    rawinv = __execute_ret(
1171        "getversion",
1172        host=host,
1173        admin_username=admin_username,
1174        admin_password=admin_password,
1175    )
1176
1177    if rawinv["retcode"] != 0:
1178        return rawinv
1179
1180    in_server = False
1181    in_switch = False
1182    in_cmc = False
1183    in_chassis = False
1184    ret = {}
1185    ret["server"] = {}
1186    ret["switch"] = {}
1187    ret["cmc"] = {}
1188    ret["chassis"] = {}
1189    for l in rawinv["stdout"].splitlines():
1190        if l.startswith("<Server>"):
1191            in_server = True
1192            in_switch = False
1193            in_cmc = False
1194            in_chassis = False
1195            continue
1196
1197        if l.startswith("<Switch>"):
1198            in_server = False
1199            in_switch = True
1200            in_cmc = False
1201            in_chassis = False
1202            continue
1203
1204        if l.startswith("<CMC>"):
1205            in_server = False
1206            in_switch = False
1207            in_cmc = True
1208            in_chassis = False
1209            continue
1210
1211        if l.startswith("<Chassis Infrastructure>"):
1212            in_server = False
1213            in_switch = False
1214            in_cmc = False
1215            in_chassis = True
1216            continue
1217
1218        if not l:
1219            continue
1220
1221        line = re.split("  +", l.strip())
1222
1223        if in_server:
1224            ret["server"][line[0]] = {
1225                k: v for d in map(mapit, fields["server"], line) for (k, v) in d.items()
1226            }
1227        if in_switch:
1228            ret["switch"][line[0]] = {
1229                k: v for d in map(mapit, fields["switch"], line) for (k, v) in d.items()
1230            }
1231        if in_cmc:
1232            ret["cmc"][line[0]] = {
1233                k: v for d in map(mapit, fields["cmc"], line) for (k, v) in d.items()
1234            }
1235        if in_chassis:
1236            ret["chassis"][line[0]] = {
1237                k: v for d in map(mapit, fields["chassis"], line) for k, v in d.items()
1238            }
1239
1240    return ret
1241
1242
1243def set_chassis_location(location, host=None, admin_username=None, admin_password=None):
1244    """
1245    Set the location of the chassis.
1246
1247    location
1248        The name of the location to be set on the chassis.
1249
1250    host
1251        The chassis host.
1252
1253    admin_username
1254        The username used to access the chassis.
1255
1256    admin_password
1257        The password used to access the chassis.
1258
1259    CLI Example:
1260
1261    .. code-block:: bash
1262
1263        salt '*' dracr.set_chassis_location location-name host=111.222.333.444
1264            admin_username=root admin_password=secret
1265
1266    """
1267    return __execute_cmd(
1268        "setsysinfo -c chassislocation {}".format(location),
1269        host=host,
1270        admin_username=admin_username,
1271        admin_password=admin_password,
1272    )
1273
1274
1275def get_chassis_location(host=None, admin_username=None, admin_password=None):
1276    """
1277    Get the location of the chassis.
1278
1279    host
1280        The chassis host.
1281
1282    admin_username
1283        The username used to access the chassis.
1284
1285    admin_password
1286        The password used to access the chassis.
1287
1288    CLI Example:
1289
1290    .. code-block:: bash
1291
1292        salt '*' dracr.set_chassis_location host=111.222.333.444
1293           admin_username=root admin_password=secret
1294
1295    """
1296    return system_info(
1297        host=host, admin_username=admin_username, admin_password=admin_password
1298    )["Chassis Information"]["Chassis Location"]
1299
1300
1301def set_chassis_datacenter(
1302    location, host=None, admin_username=None, admin_password=None
1303):
1304    """
1305    Set the location of the chassis.
1306
1307    location
1308        The name of the datacenter to be set on the chassis.
1309
1310    host
1311        The chassis host.
1312
1313    admin_username
1314        The username used to access the chassis.
1315
1316    admin_password
1317        The password used to access the chassis.
1318
1319    CLI Example:
1320
1321    .. code-block:: bash
1322
1323        salt '*' dracr.set_chassis_datacenter datacenter-name host=111.222.333.444
1324            admin_username=root admin_password=secret
1325
1326    """
1327    return set_general(
1328        "cfgLocation",
1329        "cfgLocationDatacenter",
1330        location,
1331        host=host,
1332        admin_username=admin_username,
1333        admin_password=admin_password,
1334    )
1335
1336
1337def get_chassis_datacenter(host=None, admin_username=None, admin_password=None):
1338    """
1339    Get the datacenter of the chassis.
1340
1341    host
1342        The chassis host.
1343
1344    admin_username
1345        The username used to access the chassis.
1346
1347    admin_password
1348        The password used to access the chassis.
1349
1350    CLI Example:
1351
1352    .. code-block:: bash
1353
1354        salt '*' dracr.set_chassis_location host=111.222.333.444
1355           admin_username=root admin_password=secret
1356
1357    """
1358    return get_general(
1359        "cfgLocation",
1360        "cfgLocationDatacenter",
1361        host=host,
1362        admin_username=admin_username,
1363        admin_password=admin_password,
1364    )
1365
1366
1367def set_general(
1368    cfg_sec, cfg_var, val, host=None, admin_username=None, admin_password=None
1369):
1370    return __execute_cmd(
1371        "config -g {} -o {} {}".format(cfg_sec, cfg_var, val),
1372        host=host,
1373        admin_username=admin_username,
1374        admin_password=admin_password,
1375    )
1376
1377
1378def get_general(cfg_sec, cfg_var, host=None, admin_username=None, admin_password=None):
1379    ret = __execute_ret(
1380        "getconfig -g {} -o {}".format(cfg_sec, cfg_var),
1381        host=host,
1382        admin_username=admin_username,
1383        admin_password=admin_password,
1384    )
1385
1386    if ret["retcode"] == 0:
1387        return ret["stdout"]
1388    else:
1389        return ret
1390
1391
1392def idrac_general(
1393    blade_name,
1394    command,
1395    idrac_password=None,
1396    host=None,
1397    admin_username=None,
1398    admin_password=None,
1399):
1400    """
1401    Run a generic racadm command against a particular
1402    blade in a chassis.  Blades are usually named things like
1403    'server-1', 'server-2', etc.  If the iDRAC has a different
1404    password than the CMC, then you can pass it with the
1405    idrac_password kwarg.
1406
1407    :param blade_name: Name of the blade to run the command on
1408    :param command: Command like to pass to racadm
1409    :param idrac_password: Password for the iDRAC if different from the CMC
1410    :param host: Chassis hostname
1411    :param admin_username: CMC username
1412    :param admin_password: CMC password
1413    :return: stdout if the retcode is 0, otherwise a standard cmd.run_all dictionary
1414
1415    CLI Example:
1416
1417    .. code-block:: bash
1418
1419        salt fx2 chassis.cmd idrac_general server-1 'get BIOS.SysProfileSettings'
1420
1421    """
1422
1423    module_network = network_info(host, admin_username, admin_password, blade_name)
1424
1425    if idrac_password is not None:
1426        password = idrac_password
1427    else:
1428        password = admin_password
1429
1430    idrac_ip = module_network["Network"]["IP Address"]
1431
1432    ret = __execute_ret(
1433        command, host=idrac_ip, admin_username="root", admin_password=password
1434    )
1435
1436    if ret["retcode"] == 0:
1437        return ret["stdout"]
1438    else:
1439        return ret
1440
1441
1442def _update_firmware(cmd, host=None, admin_username=None, admin_password=None):
1443
1444    if not admin_username:
1445        admin_username = __pillar__["proxy"]["admin_username"]
1446    if not admin_username:
1447        admin_password = __pillar__["proxy"]["admin_password"]
1448
1449    ret = __execute_ret(
1450        cmd, host=host, admin_username=admin_username, admin_password=admin_password
1451    )
1452
1453    if ret["retcode"] == 0:
1454        return ret["stdout"]
1455    else:
1456        return ret
1457
1458
1459def bare_rac_cmd(cmd, host=None, admin_username=None, admin_password=None):
1460    ret = __execute_ret(
1461        "{}".format(cmd),
1462        host=host,
1463        admin_username=admin_username,
1464        admin_password=admin_password,
1465    )
1466
1467    if ret["retcode"] == 0:
1468        return ret["stdout"]
1469    else:
1470        return ret
1471
1472
1473def update_firmware(filename, host=None, admin_username=None, admin_password=None):
1474    """
1475    Updates firmware using local firmware file
1476
1477    .. code-block:: bash
1478
1479         salt dell dracr.update_firmware firmware.exe
1480
1481    This executes the following command on your FX2
1482    (using username and password stored in the pillar data)
1483
1484    .. code-block:: bash
1485
1486         racadm update –f firmware.exe -u user –p pass
1487
1488    """
1489    if os.path.exists(filename):
1490        return _update_firmware(
1491            "update -f {}".format(filename),
1492            host=None,
1493            admin_username=None,
1494            admin_password=None,
1495        )
1496    else:
1497        raise CommandExecutionError("Unable to find firmware file {}".format(filename))
1498
1499
1500def update_firmware_nfs_or_cifs(
1501    filename, share, host=None, admin_username=None, admin_password=None
1502):
1503    """
1504    Executes the following for CIFS
1505    (using username and password stored in the pillar data)
1506
1507    .. code-block:: bash
1508
1509         racadm update -f <updatefile> -u user –p pass -l //IP-Address/share
1510
1511    Or for NFS
1512    (using username and password stored in the pillar data)
1513
1514    .. code-block:: bash
1515
1516          racadm update -f <updatefile> -u user –p pass -l IP-address:/share
1517
1518
1519    Salt command for CIFS:
1520
1521    .. code-block:: bash
1522
1523         salt dell dracr.update_firmware_nfs_or_cifs \
1524         firmware.exe //IP-Address/share
1525
1526
1527    Salt command for NFS:
1528
1529    .. code-block:: bash
1530
1531         salt dell dracr.update_firmware_nfs_or_cifs \
1532         firmware.exe IP-address:/share
1533    """
1534    if os.path.exists(filename):
1535        return _update_firmware(
1536            "update -f {} -l {}".format(filename, share),
1537            host=None,
1538            admin_username=None,
1539            admin_password=None,
1540        )
1541    else:
1542        raise CommandExecutionError("Unable to find firmware file {}".format(filename))
1543
1544
1545# def get_idrac_nic()
1546