1#!/usr/local/bin/python3.8
2# -*- coding: utf-8 -*-
3
4# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
5
6from __future__ import absolute_import, division, print_function
7__metaclass__ = type
8
9ANSIBLE_METADATA = {'metadata_version': '1.1',
10                    'status': ['preview'],
11                    'supported_by': 'community'}
12
13DOCUMENTATION = r'''
14---
15module: ucs_service_profile_template
16short_description: Configures Service Profile Templates on Cisco UCS Manager
17description:
18- Configures Service Profile Templates on Cisco UCS Manager.
19extends_documentation_fragment: cisco.ucs.ucs
20options:
21  state:
22    description:
23    - If C(present), will verify Service Profile Templates are present and will create if needed.
24    - If C(absent), will verify Service Profile Templates are absent and will delete if needed.
25    choices: [present, absent]
26    default: present
27  name:
28    description:
29    - The name of the service profile template.
30    - This name can be between 2 and 32 alphanumeric characters.
31    - "You cannot use spaces or any special characters other than - (hyphen), \"_\" (underscore), : (colon), and . (period)."
32    - This name must be unique across all service profiles and service profile templates within the same organization.
33    required: yes
34  template_type:
35    description:
36    - "The template type field which can be one of the following:"
37    - "initial-template — Any service profiles created from this template are not updated if the template changes."
38    - "updating-template — Any service profiles created from this template are updated if the template changes."
39    choices: [initial-template, updating-template]
40    default: initial-template
41  uuid_pool:
42    description:
43    - Specifies how the UUID will be set on a server associated with a service profile created from this template.
44    - "The uuid_pool option can be the name of the UUID pool to use or '' (the empty string)."
45    - An empty string will use the UUID assigned to the server by the manufacturer, and the
46    - UUID remains unassigned until a service profile created from this template is associated with a server. At that point,
47    - the UUID is set to the UUID value assigned to the server by the manufacturer. If the service profile is later moved to
48    - a different server, the UUID is changed to match the new server."
49    default: default
50  description:
51    description:
52    - A user-defined description of the service profile template.
53    - Enter up to 256 characters.
54    - "You can use any characters or spaces except the following:"
55    - "` (accent mark), \ (backslash), ^ (carat), \" (double quote), = (equal sign), > (greater than), < (less than), or ' (single quote)."
56    aliases: [ descr ]
57  storage_profile:
58    description:
59    - The name of the storage profile you want to associate with service profiles created from this template
60  local_disk_policy:
61    description:
62    - The name of the local disk policy you want to associate with service profiles created from this template.
63  lan_connectivity_policy:
64    description:
65    - The name of the LAN connectivity policy you want to associate with service profiles created from this template.
66  iqn_pool:
67    description:
68    - The name of the IQN pool (initiator) you want to apply to all iSCSI vNICs for service profiles created from this template.
69  san_connectivity_policy:
70    description:
71    - The name of the SAN connectivity policy you want to associate with service profiles created from this template.
72  vmedia_policy:
73    description:
74    - The name of the vMedia policy you want to associate with service profiles created from this template.
75  boot_policy:
76    description:
77    - The name of the boot order policy you want to associate with service profiles created from this template.
78    default: default
79  maintenance_policy:
80    description:
81    - The name of the maintenance policy you want to associate with service profiles created from this template.
82  server_pool:
83    description:
84    - The name of the server pool you want to associate with this service profile template.
85  server_pool_qualification:
86    description:
87    - The name of the server pool policy qualificaiton you want to use for this service profile template.
88  power_state:
89    description:
90    - The power state to be applied when a service profile created from this template is associated with a server.
91    choices: [up, down]
92    default: up
93  host_firmware_package:
94    description:
95    - The name of the host firmware package you want to associate with service profiles created from this template.
96  bios_policy:
97    description:
98    - The name of the BIOS policy you want to associate with service profiles created from this template.
99  ipmi_access_profile:
100    description:
101    - The name of the IPMI access profile you want to associate with service profiles created from this template.
102  sol_policy:
103    description:
104    - The name of the Serial over LAN (SoL) policy you want to associate with service profiles created from this template.
105  mgmt_ip_state:
106    description:
107    - The state for the Outband Management IP pool you want to use with service profiles created from this template.
108    choices: [none, pooled]
109    default: pooled
110  mgmt_ip_pool:
111    description:
112    - The name of the Outband Management IP pool you want to use with service profiles created from this template.
113    default: ext-mgmt
114  power_control_policy:
115    description:
116    - The name of the power control policy you want to associate with service profiles created from this template.
117    default: default
118  power_sync_policy:
119    description:
120    - The name of the power sync policy you want to associate with service profiles created from this template.
121  scrub_policy:
122    description:
123    - The name of the scrub policy you want to associate with service profiles created from this template.
124  kvm_mgmt_policy:
125    description:
126    - The name of the KVM management policy you want to associate with service profiles created from this template.
127  graphics_card_policy:
128    description:
129    - The name of the graphics card policy you want to associate with service profiles created from this template.
130  threshold_policy:
131    description:
132    - The name of the threshold policy you want to associate with service profiles created from this template.
133    default: default
134  user_label:
135    description:
136    - The User Label you want to assign to service profiles created from this template.
137  mgmt_interface_mode:
138    description:
139    - The Management Interface you want to assign to service profiles created from this template.
140    choices: ['', in-band]
141  mgmt_vnet_name:
142    description:
143    - A VLAN selected from the associated VLAN group.
144  mgmt_inband_pool_name:
145    description:
146    - How the inband management IPv4 address is derived for the server associated with this service profile.
147  org_dn:
148    description:
149    - Org dn (distinguished name)
150    default: org-root
151requirements:
152- ucsmsdk
153author:
154- David Soper (@dsoper2)
155- CiscoUcs (@CiscoUcs)
156version_added: '2.8'
157'''
158
159EXAMPLES = r'''
160- name: Configure Service Profile Template with LAN/SAN Connectivity and all other options defaulted
161  cisco.ucs.ucs_service_profile_template:
162    hostname: 172.16.143.150
163    username: admin
164    password: password
165    name: DEE-Ctrl
166    template_type: updating-template
167    uuid_pool: UUID-Pool
168    storage_profile: DEE-StgProf
169    lan_connectivity_policy: Cntr-FC-Boot
170    iqn_pool: iSCSI-Boot-A
171    san_connectivity_policy: Cntr-FC-Boot
172    boot_policy: DEE-vMedia
173    maintenance_policy: default
174    server_pool: Container-Pool
175    host_firmware_package: 3.1.2b
176    bios_policy: Docker
177
178- name: Remove Service Profile Template
179  cisco.ucs.ucs_service_profile_template:
180    hostname: 172.16.143.150
181    username: admin
182    password: password
183    name: DEE-Ctrl
184    state: absent
185'''
186
187RETURN = r'''
188#
189'''
190
191from ansible.module_utils.basic import AnsibleModule
192from ansible_collections.cisco.ucs.plugins.module_utils.ucs import UCSModule, ucs_argument_spec
193
194
195def configure_service_profile_template(ucs, module):
196    from ucsmsdk.mometa.ls.LsServer import LsServer
197    from ucsmsdk.mometa.vnic.VnicConnDef import VnicConnDef
198    from ucsmsdk.mometa.vnic.VnicIScsiNode import VnicIScsiNode
199    from ucsmsdk.mometa.ls.LsRequirement import LsRequirement
200    from ucsmsdk.mometa.ls.LsPower import LsPower
201    from ucsmsdk.mometa.lstorage.LstorageProfileBinding import LstorageProfileBinding
202    from ucsmsdk.mometa.mgmt.MgmtInterface import MgmtInterface
203    from ucsmsdk.mometa.mgmt.MgmtVnet import MgmtVnet
204    from ucsmsdk.mometa.vnic.VnicIpV4MgmtPooledAddr import VnicIpV4MgmtPooledAddr
205
206    if not module.check_mode:
207        try:
208            # create if mo does not already exist
209            mo = LsServer(
210                parent_mo_or_dn=module.params['org_dn'],
211                bios_profile_name=module.params['bios_policy'],
212                boot_policy_name=module.params['boot_policy'],
213                descr=module.params['description'],
214                ext_ip_state=module.params['mgmt_ip_state'],
215                ext_ip_pool_name=module.params['mgmt_ip_pool'],
216                # graphics_card_policy_name=module.params['graphics_card_policy'],
217                host_fw_policy_name=module.params['host_firmware_package'],
218                ident_pool_name=module.params['uuid_pool'],
219                kvm_mgmt_policy_name=module.params['kvm_mgmt_policy'],
220                local_disk_policy_name=module.params['local_disk_policy'],
221                maint_policy_name=module.params['maintenance_policy'],
222                mgmt_access_policy_name=module.params['ipmi_access_profile'],
223                name=module.params['name'],
224                power_policy_name=module.params['power_control_policy'],
225                power_sync_policy_name=module.params['power_sync_policy'],
226                scrub_policy_name=module.params['scrub_policy'],
227                sol_policy_name=module.params['sol_policy'],
228                stats_policy_name=module.params['threshold_policy'],
229                type=module.params['template_type'],
230                usr_lbl=module.params['user_label'],
231                vmedia_policy_name=module.params['vmedia_policy'],
232            )
233
234            if module.params['storage_profile']:
235                # Storage profile
236                mo_1 = LstorageProfileBinding(
237                    parent_mo_or_dn=mo,
238                    storage_profile_name=module.params['storage_profile'],
239                )
240
241            if module.params['mgmt_interface_mode']:
242                # Management Interface
243                mo_1 = MgmtInterface(
244                    parent_mo_or_dn=mo,
245                    mode=module.params['mgmt_interface_mode'],
246                    ip_v4_state='pooled',
247                )
248                mo_2 = MgmtVnet(
249                    parent_mo_or_dn=mo_1,
250                    id='1',
251                    name=module.params['mgmt_vnet_name'],
252                )
253                VnicIpV4MgmtPooledAddr(
254                    parent_mo_or_dn=mo_2,
255                    name=module.params['mgmt_inband_pool_name'],
256                )
257
258            # LAN/SAN connectivity policy
259            mo_1 = VnicConnDef(
260                parent_mo_or_dn=mo,
261                lan_conn_policy_name=module.params['lan_connectivity_policy'],
262                san_conn_policy_name=module.params['san_connectivity_policy'],
263            )
264
265            if module.params['iqn_pool']:
266                # IQN pool
267                mo_1 = VnicIScsiNode(
268                    parent_mo_or_dn=mo,
269                    iqn_ident_pool_name=module.params['iqn_pool']
270                )
271
272            # power state
273            admin_state = 'admin-' + module.params['power_state']
274            mo_1 = LsPower(
275                parent_mo_or_dn=mo,
276                state=admin_state,
277            )
278
279            if module.params['server_pool']:
280                # server pool
281                mo_1 = LsRequirement(
282                    parent_mo_or_dn=mo,
283                    name=module.params['server_pool'],
284                    qualifier=module.params['server_pool_qualification'],
285                )
286
287            ucs.login_handle.add_mo(mo, True)
288            ucs.login_handle.commit()
289        except Exception as e:  # generic Exception handling because SDK can throw a variety of exceptions
290            ucs.result['msg'] = "setup error: %s " % str(e)
291            module.fail_json(**ucs.result)
292
293    ucs.result['changed'] = True
294
295
296def check_storage_profile_props(ucs, module, dn):
297    props_match = False
298
299    child_dn = dn + '/profile-binding'
300    mo_1 = ucs.login_handle.query_dn(child_dn)
301    if mo_1:
302        kwargs = dict(storage_profile_name=module.params['storage_profile'])
303        if mo_1.check_prop_match(**kwargs):
304            props_match = True
305    elif not module.params['storage_profile']:
306        # no stroage profile mo or desired state
307        props_match = True
308
309    return props_match
310
311
312def check_connectivity_policy_props(ucs, module, dn):
313    props_match = False
314
315    child_dn = dn + '/conn-def'
316    mo_1 = ucs.login_handle.query_dn(child_dn)
317    if mo_1:
318        kwargs = dict(lan_conn_policy_name=module.params['lan_connectivity_policy'])
319        kwargs['san_conn_policy_name'] = module.params['san_connectivity_policy']
320        if mo_1.check_prop_match(**kwargs):
321            props_match = True
322    elif not module.params['lan_connectivity_policy'] and not module.params['san_connectivity_policy']:
323        # no mo and no desired state
324        props_match = True
325
326    return props_match
327
328
329def check_iqn_pool_props(ucs, module, dn):
330    props_match = False
331
332    child_dn = dn + '/iscsi-node'
333    mo_1 = ucs.login_handle.query_dn(child_dn)
334    if mo_1:
335        kwargs = dict(iqn_ident_pool_name=module.params['iqn_pool'])
336        if mo_1.check_prop_match(**kwargs):
337            props_match = True
338    elif not module.params['iqn_pool']:
339        # no mo and no desired state
340        props_match = True
341
342    return props_match
343
344
345def check_inband_management_props(ucs, module, dn):
346    props_match = False
347
348    child_dn = dn + '/iface-in-band'
349    mo_1 = ucs.login_handle.query_dn(child_dn)
350    if mo_1:
351        kwargs = dict(mode=module.params['mgmt_interface_mode'])
352        if mo_1.check_prop_match(**kwargs):
353            child_dn = child_dn + '/network'
354            mo_2 = ucs.login_handle.query_dn(child_dn)
355            if mo_2:
356                kwargs = dict(name=module.params['mgmt_vnet_name'])
357                if mo_2.check_prop_match(**kwargs):
358                    child_dn = child_dn + '/ipv4-pooled-addr'
359                    mo_3 = ucs.login_handle.query_dn(child_dn)
360                    if mo_3:
361                        kwargs = dict(name=module.params['mgmt_inband_pool_name'])
362                        if mo_3.check_prop_match(**kwargs):
363                            props_match = True
364    elif not module.params['mgmt_interface_mode']:
365        # no mo and no desired state
366        props_match = True
367
368    return props_match
369
370
371def check_power_props(ucs, module, dn):
372    props_match = False
373
374    child_dn = dn + '/power'
375    mo_1 = ucs.login_handle.query_dn(child_dn)
376    if mo_1:
377        kwargs = dict(state=module.params['power_state'])
378        if mo_1.check_prop_match(**kwargs):
379            props_match = True
380    elif not module.params['power_state']:
381        # no mo and no desired state
382        props_match = True
383
384    return props_match
385
386
387def check_server_pool(ucs, module, dn):
388    props_match = False
389
390    child_dn = dn + '/pn-req'
391    mo_1 = ucs.login_handle.query_dn(child_dn)
392    if mo_1:
393        kwargs = dict(name=module.params['server_pool'])
394        kwargs['qualifier'] = module.params['server_pool_qualification']
395        if mo_1.check_prop_match(**kwargs):
396            props_match = True
397    elif not module.params['server_pool']:
398        # no pn-req object and no server pool name provided
399        props_match = True
400
401    return props_match
402
403
404def check_serivce_profile_templates_props(ucs, module, mo, dn):
405    props_match = False
406
407    # check top-level mo props
408    kwargs = dict(bios_profile_name=module.params['bios_policy'])
409    kwargs['boot_policy_name'] = module.params['boot_policy']
410    kwargs['descr'] = module.params['description']
411    kwargs['ext_ip_state'] = module.params['mgmt_ip_state']
412    kwargs['ext_ip_pool_name'] = module.params['mgmt_ip_pool']
413    # kwargs['graphics_card_policy_name'] = module.params['graphics_card_policy']
414    kwargs['host_fw_policy_name'] = module.params['host_firmware_package']
415    kwargs['ident_pool_name'] = module.params['uuid_pool']
416    kwargs['kvm_mgmt_policy_name'] = module.params['kvm_mgmt_policy']
417    kwargs['local_disk_policy_name'] = module.params['local_disk_policy']
418    kwargs['maint_policy_name'] = module.params['maintenance_policy']
419    kwargs['mgmt_access_policy_name'] = module.params['ipmi_access_profile']
420    kwargs['power_policy_name'] = module.params['power_control_policy']
421    kwargs['power_sync_policy_name'] = module.params['power_sync_policy']
422    kwargs['scrub_policy_name'] = module.params['scrub_policy']
423    kwargs['sol_policy_name'] = module.params['sol_policy']
424    kwargs['stats_policy_name'] = module.params['threshold_policy']
425    kwargs['type'] = module.params['template_type']
426    kwargs['usr_lbl'] = module.params['user_label']
427    kwargs['vmedia_policy_name'] = module.params['vmedia_policy']
428
429    if mo.check_prop_match(**kwargs):
430        # top-level props match, check next level mo/props
431        # code below should discontinue checks once any mismatch is found
432
433        # check storage profile 1st
434        props_match = check_storage_profile_props(ucs, module, dn)
435
436        if props_match:
437            props_match = check_connectivity_policy_props(ucs, module, dn)
438
439        if props_match:
440            props_match = check_iqn_pool_props(ucs, module, dn)
441
442        if props_match:
443            props_match = check_inband_management_props(ucs, module, dn)
444
445        if props_match:
446            props_match = check_power_props(ucs, module, dn)
447
448        if props_match:
449            props_match = check_server_pool(ucs, module, dn)
450
451    return props_match
452
453
454def main():
455    argument_spec = ucs_argument_spec
456    argument_spec.update(
457        org_dn=dict(type='str', default='org-root'),
458        name=dict(type='str', required=True),
459        bios_policy=dict(type='str', default=''),
460        boot_policy=dict(type='str', default='default'),
461        description=dict(type='str', aliases=['descr'], default=''),
462        mgmt_ip_state=dict(type='str', default='pooled'),
463        mgmt_ip_pool=dict(type='str', default='ext-mgmt'),
464        graphics_card_policy=dict(type='str', default=''),
465        host_firmware_package=dict(type='str', default=''),
466        uuid_pool=dict(type='str', default='default'),
467        kvm_mgmt_policy=dict(type='str', default=''),
468        local_disk_policy=dict(type='str', default=''),
469        maintenance_policy=dict(type='str', default=''),
470        ipmi_access_profile=dict(type='str', default=''),
471        power_control_policy=dict(type='str', default='default'),
472        power_sync_policy=dict(type='str', default=''),
473        scrub_policy=dict(type='str', default=''),
474        sol_policy=dict(type='str', default=''),
475        threshold_policy=dict(type='str', default='default'),
476        template_type=dict(type='str', default='initial-template', choices=['initial-template', 'updating-template']),
477        user_label=dict(type='str', default=''),
478        vmedia_policy=dict(type='str', default=''),
479        storage_profile=dict(type='str', default=''),
480        lan_connectivity_policy=dict(type='str', default=''),
481        iqn_pool=dict(type='str', default=''),
482        san_connectivity_policy=dict(type='str', default=''),
483        server_pool=dict(type='str', default=''),
484        server_pool_qualification=dict(type='str', default=''),
485        power_state=dict(type='str', default='up', choices=['up', 'down']),
486        mgmt_interface_mode=dict(type='str', default='', choices=['', 'in-band']),
487        mgmt_vnet_name=dict(type='str', default=''),
488        mgmt_inband_pool_name=dict(type='str', default=''),
489        state=dict(type='str', default='present', choices=['present', 'absent']),
490    )
491
492    module = AnsibleModule(
493        argument_spec,
494        supports_check_mode=True,
495    )
496    ucs = UCSModule(module)
497    # UCSModule creation above verifies ucsmsdk is present and exits on failure.
498    # Additional imports are done below or in called functions.
499
500    ucs.result['changed'] = False
501    props_match = False
502    # dn is <org_dn>/ls-<name>
503    dn = module.params['org_dn'] + '/ls-' + module.params['name']
504
505    mo = ucs.login_handle.query_dn(dn)
506    if mo:
507        if module.params['state'] == 'absent':
508            # mo must exist but all properties do not have to match
509            if not module.check_mode:
510                ucs.login_handle.remove_mo(mo)
511                ucs.login_handle.commit()
512            ucs.result['changed'] = True
513        else:  # state == 'present'
514            props_match = check_serivce_profile_templates_props(ucs, module, mo, dn)
515
516    if module.params['state'] == 'present' and not props_match:
517        configure_service_profile_template(ucs, module)
518
519    module.exit_json(**ucs.result)
520
521
522if __name__ == '__main__':
523    main()
524