1"""
2Manage IPMI devices over LAN
3============================
4
5The following configuration defaults can be defined in the
6minion, master config or pillar:
7
8.. code-block:: yaml
9
10    ipmi.config:
11        api_host: 127.0.0.1
12        api_user: admin
13        api_pass: apassword
14        api_port: 623
15        api_kg: None
16
17Every call can override the config defaults:
18
19.. code-block:: yaml
20
21    ensure myipmi system is set to network boot:
22        ipmi.boot_device:
23            - name: network
24            - api_host: myipmi.hostname.com
25            - api_user: root
26            - api_pass: apassword
27            - api_kg: None
28
29    ensure myipmi system is powered on:
30        ipmi.power:
31            - name: boot
32            - api_host: myipmi.hostname.com
33            - api_user: root
34            - api_pass: apassword
35"""
36
37
38def __virtual__():
39    IMPORT_ERR = None
40    try:
41        from pyghmi.ipmi import command  # pylint: disable=unused-import
42    except Exception as exc:  # pylint: disable=broad-except
43        IMPORT_ERR = str(exc)
44    return (IMPORT_ERR is None, IMPORT_ERR)
45
46
47def boot_device(name="default", **kwargs):
48    """
49    Request power state change
50
51    name = ``default``
52        * network -- Request network boot
53        * hd -- Boot from hard drive
54        * safe -- Boot from hard drive, requesting 'safe mode'
55        * optical -- boot from CD/DVD/BD drive
56        * setup -- Boot into setup utility
57        * default -- remove any IPMI directed boot device request
58
59    kwargs
60        - api_host=localhost
61        - api_user=admin
62        - api_pass=
63        - api_port=623
64        - api_kg=None
65    """
66    ret = {"name": name, "result": False, "comment": "", "changes": {}}
67    org = __salt__["ipmi.get_bootdev"](**kwargs)
68    if "bootdev" in org:
69        org = org["bootdev"]
70
71    if org == name:
72        ret["result"] = True
73        ret["comment"] = "system already in this state"
74        return ret
75
76    if __opts__["test"]:
77        ret["comment"] = "would change boot device"
78        ret["result"] = None
79        ret["changes"] = {"old": org, "new": name}
80        return ret
81
82    outdddd = __salt__["ipmi.set_bootdev"](bootdev=name, **kwargs)
83    ret["comment"] = "changed boot device"
84    ret["result"] = True
85    ret["changes"] = {"old": org, "new": name}
86    return ret
87
88
89def power(name="power_on", wait=300, **kwargs):
90    """
91    Request power state change
92
93    name
94        Ensure power state one of:
95            * power_on -- system turn on
96            * power_off -- system turn off (without waiting for OS)
97            * shutdown -- request OS proper shutdown
98            * reset -- reset (without waiting for OS)
99            * boot -- If system is off, then 'on', else 'reset'
100
101    wait
102        wait X seconds for the job to complete before forcing.
103        (defaults to 300 seconds)
104
105    kwargs
106        - api_host=localhost
107        - api_user=admin
108        - api_pass=
109        - api_port=623
110        - api_kg=None
111    """
112    ret = {"name": name, "result": False, "comment": "", "changes": {}}
113    org = __salt__["ipmi.get_power"](**kwargs)
114
115    state_map = {
116        "off": "off",
117        "on": "on",
118        "power_off": "off",
119        "power_on": "on",
120        "shutdown": "off",
121        "reset": "na",
122        "boot": "na",
123    }
124
125    if org == state_map[name]:
126        ret["result"] = True
127        ret["comment"] = "system already in this state"
128        return ret
129
130    if __opts__["test"]:
131        ret["comment"] = "would power: {} system".format(name)
132        ret["result"] = None
133        ret["changes"] = {"old": org, "new": name}
134        return ret
135
136    outdddd = __salt__["ipmi.set_power"](name, wait=wait, **kwargs)
137    ret["comment"] = "changed system power"
138    ret["result"] = True
139    ret["changes"] = {"old": org, "new": name}
140    return ret
141
142
143def user_present(
144    name,
145    uid,
146    password,
147    channel=14,
148    callback=False,
149    link_auth=True,
150    ipmi_msg=True,
151    privilege_level="administrator",
152    **kwargs
153):
154    """
155    Ensure IPMI user and user privileges.
156
157    name
158        name of user (limit 16 bytes)
159
160    uid
161        user id number (1 to 7)
162
163    password
164        user password (limit 16 bytes)
165
166    channel
167        ipmi channel defaults to 14 for auto
168
169    callback
170        User Restricted to Callback
171
172        False = User Privilege Limit is determined by the User Privilege Limit
173            parameter privilege_level, for both callback and non-callback connections.
174
175        True  = User Privilege Limit is determined by the privilege_level
176            parameter for callback connections, but is restricted to Callback
177            level for non-callback connections. Thus, a user can only initiate
178            a Callback when they 'call in' to the BMC, but once the callback
179            connection has been made, the user could potentially establish a
180            session as an Operator.
181
182    link_auth
183        User Link authentication
184        True/False
185        user name and password information will be used for link
186        authentication, e.g. PPP CHAP) for the given channel. Link
187        authentication itself is a global setting for the channel and is
188        enabled/disabled via the serial/modem configuration parameters.
189
190    ipmi_msg
191        User IPMI Messaging
192        True/False
193        user name and password information will be used for IPMI
194        Messaging. In this case, 'IPMI Messaging' refers to the ability to
195        execute generic IPMI commands that are not associated with a
196        particular payload type. For example, if IPMI Messaging is disabled for
197        a user, but that user is enabled for activating the SOL
198        payload type, then IPMI commands associated with SOL and session
199        management, such as Get SOL Configuration Parameters and Close Session
200        are available, but generic IPMI commands such as Get SEL Time are
201        unavailable.)
202        ipmi_msg
203
204    privilege_level
205        * callback
206        * user
207        * operator
208        * administrator
209        * proprietary
210        * no_access
211
212    kwargs
213        - api_host=localhost
214        - api_user=admin
215        - api_pass=
216        - api_port=623
217        - api_kg=None
218    """
219    ret = {"name": name, "result": False, "comment": "", "changes": {}}
220    org_user = __salt__["ipmi.get_user"](uid=uid, channel=channel, **kwargs)
221
222    change = False
223    if org_user["access"]["callback"] != callback:
224        change = True
225    if org_user["access"]["link_auth"] != link_auth:
226        change = True
227    if org_user["access"]["ipmi_msg"] != ipmi_msg:
228        change = True
229    if org_user["access"]["privilege_level"] != privilege_level:
230        change = True
231    if (
232        __salt__["ipmi.set_user_password"](
233            uid, mode="test_password", password=password, **kwargs
234        )
235        is False
236    ):
237        change = True
238
239    if change is False:
240        ret["result"] = True
241        ret["comment"] = "user already present"
242        return ret
243
244    if __opts__["test"]:
245        ret["comment"] = "would (re)create user"
246        ret["result"] = None
247        ret["changes"] = {"old": org_user, "new": name}
248        return ret
249
250    __salt__["ipmi.ensure_user"](
251        uid,
252        name,
253        password,
254        channel,
255        callback,
256        link_auth,
257        ipmi_msg,
258        privilege_level,
259        **kwargs
260    )
261    current_user = __salt__["ipmi.get_user"](uid=uid, channel=channel, **kwargs)
262    ret["comment"] = "(re)created user"
263    ret["result"] = True
264    ret["changes"] = {"old": org_user, "new": current_user}
265    return ret
266
267
268def user_absent(name, channel=14, **kwargs):
269    """
270    Remove user
271    Delete all user (uid) records having the matching name.
272
273    name
274        string name of user to delete
275
276    channel
277        channel to remove user access from defaults to 14 for auto.
278
279    kwargs
280        - api_host=localhost
281        - api_user=admin
282        - api_pass=
283        - api_port=623
284        - api_kg=None
285    """
286    ret = {"name": name, "result": False, "comment": "", "changes": {}}
287    user_id_list = __salt__["ipmi.get_name_uids"](name, channel, **kwargs)
288
289    if len(user_id_list) == 0:
290        ret["result"] = True
291        ret["comment"] = "user already absent"
292        return ret
293
294    if __opts__["test"]:
295        ret["comment"] = "would delete user(s)"
296        ret["result"] = None
297        ret["changes"] = {"delete": user_id_list}
298        return ret
299
300    for uid in user_id_list:
301        __salt__["ipmi.delete_user"](uid, channel, **kwargs)
302
303    ret["comment"] = "user(s) removed"
304    ret["changes"] = {"old": user_id_list, "new": "None"}
305    return ret
306