From 3a86579b692ab4d74c5b0845f0fb85d6897afb53 Mon Sep 17 00:00:00 2001 From: Dag Wieers Date: Mon, 7 Aug 2017 18:49:29 +0200 Subject: [PATCH] vcenter_license: New module to manage vCenter licenses (#25143) This module supports check-mode and diff output. It also includes basic integration tests. This PR fixes #24971 --- CHANGELOG.md | 1 + .../modules/cloud/vmware/vcenter_license.py | 208 ++++++++++++++++++ .../targets/vcenter_license/aliases | 2 + .../targets/vcenter_license/tasks/main.yml | 76 +++++++ 4 files changed, 287 insertions(+) create mode 100644 lib/ansible/modules/cloud/vmware/vcenter_license.py create mode 100644 test/integration/targets/vcenter_license/aliases create mode 100644 test/integration/targets/vcenter_license/tasks/main.yml diff --git a/CHANGELOG.md b/CHANGELOG.md index fb90419508d..dcff6b28d9e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -288,6 +288,7 @@ Ansible Changes By Release * rundeck_project - sensu_silence - vmware + * vcenter_license * vmware_guest_find - windows * win_defrag diff --git a/lib/ansible/modules/cloud/vmware/vcenter_license.py b/lib/ansible/modules/cloud/vmware/vcenter_license.py new file mode 100644 index 00000000000..56f5fe559cd --- /dev/null +++ b/lib/ansible/modules/cloud/vmware/vcenter_license.py @@ -0,0 +1,208 @@ +#!/usr/bin/python + +# Copyright 2017, Dag Wieers +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.0', + 'status': ['preview'], + 'supported_by': 'community'} + +DOCUMENTATION = r''' +module: vcenter_license +short_description: Manage VMware vCenter license keys +description: +- Add and delete vCenter license keys. +version_added: '2.4' +author: Dag Wieers (@dagwieers) +requirements: +- pyVmomi +options: + hostname: + description: + - The hostname or IP address of the vSphere vCenter. + required: yes + username: + description: + - The username to log into the vSphere vCenter. + required: yes + aliases: [admin, user] + password: + description: + - The password to log into to the vSphere vCenter. + required: yes + aliases: [pass, pwd] + labels: + description: + - The optional labels of the license key to manage in vSphere vCenter. + - This is dictionary with key/value pair. + license: + description: + - The license key to manage in vSphere vCenter. + required: yes + state: + description: + - Whether to add (C(present)) or remove (C(absent)) the license key. + choices: [absent, present] + default: present +notes: +- This module will also auto-assign the current vCenter to the license key + if the product matches the license key, and vCenter us currently assigned + an evaluation license only. +- The evaluation license (00000-00000-00000-00000-00000) is not listed + when unused. +''' + +EXAMPLES = r''' +- name: Add a new vCenter license + vcenter_license: + hostname: '{{ vcenter_hostname }}' + username: '{{ vcenter_username }}' + password: '{{ vcenter_password }}' + license: f600d-21ae3-5592b-249e0-cc341 + state: present + delegate_to: localhost + +- name: Remove an (unused) vCenter license + vmware_license: + hostname: '{{ vcenter_hostname }}' + username: '{{ vcenter_username }}' + password: '{{ vcenter_password }}' + license: f600d-21ae3-5592b-249e0-cc341 + state: absent + delegate_to: localhost +''' + +RETURN = r''' +licenses: + description: list of license keys after module executed + returned: always + type: list + sample: + - f600d-21ae3-5592b-249e0-cc341 + - 143cc-0e942-b2955-3ea12-d006f +''' + +try: + from pyVmomi import vim, vmodl + HAS_PYVMOMI = True +except ImportError: + HAS_PYVMOMI = False + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.vmware import connect_to_api, vmware_argument_spec + + +def find_key(licenses, license): + for item in licenses: + if item.licenseKey == license: + return item + return None + + +def list_keys(licenses): + keys = [] + for item in licenses: + # Filter out evaluation license key + if item.used is None: + continue + keys.append(item.licenseKey) + return keys + + +def main(): + argument_spec = vmware_argument_spec() + argument_spec.update(dict( + labels=dict(type='dict', default=dict(source='ansible')), + license=dict(type='str', required=True), + state=dict(type='str', default='present', choices=['absent', 'present']), + )) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + ) + + license = module.params['license'] + state = module.params['state'] + + # FIXME: This does not seem to work on vCenter v6.0 + labels = [] + for k in module.params['labels']: + kv = vim.KeyValue() + kv.key = k + kv.value = module.params['labels'][k] + labels.append(kv) + + result = dict( + changed=False, + diff=dict(), + ) + + if not HAS_PYVMOMI: + module.fail_json(msg='pyvmomi is required for this module') + + content = connect_to_api(module) + lm = content.licenseManager + + result['licenses'] = list_keys(lm.licenses) + if module._diff: + result['diff']['before'] = '\n'.join(result['licenses']) + '\n' + + if state == 'present' and license not in result['licenses']: + + result['changed'] = True + if module.check_mode: + result['licenses'].append(license) + else: + lm.AddLicense(license, labels) + + # Automatically assign to current vCenter, if needed + key = find_key(lm.licenses, license) + if content.about.name in key.name: + try: + lam = lm.licenseAssignmentManager + lam.UpdateAssignedLicense(entity=content.about.instanceUuid, licenseKey=license) + except: + module.warn('Could not assign "%s" (%s) to vCenter.' % (license, key.name)) + + result['licenses'] = list_keys(lm.licenses) + if module._diff: + result['diff']['after'] = '\n'.join(result['licenses']) + '\n' + + elif state == 'absent' and license in result['licenses']: + + # Check if key is in use + key = find_key(lm.licenses, license) + if key.used > 0: + module.fail_json(msg='Cannot remove key "%s", still in use %s time(s).' % (license, key.used)) + + result['changed'] = True + if module.check_mode: + result['licenses'].remove(license) + else: + lm.RemoveLicense(license) + result['licenses'] = list_keys(lm.licenses) + if module._diff: + result['diff']['after'] = '\n'.join(result['licenses']) + '\n' + + module.exit_json(**result) + +if __name__ == '__main__': + main() diff --git a/test/integration/targets/vcenter_license/aliases b/test/integration/targets/vcenter_license/aliases new file mode 100644 index 00000000000..1c56b8da49a --- /dev/null +++ b/test/integration/targets/vcenter_license/aliases @@ -0,0 +1,2 @@ +posix/ci/cloud/vcenter +cloud/vcenter diff --git a/test/integration/targets/vcenter_license/tasks/main.yml b/test/integration/targets/vcenter_license/tasks/main.yml new file mode 100644 index 00000000000..8d32212c494 --- /dev/null +++ b/test/integration/targets/vcenter_license/tasks/main.yml @@ -0,0 +1,76 @@ +# Test code for the vcenter license module. +# (c) 2017, Dag Wieers + +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . + +- name: Make sure pyvmomi is installed + pip: + name: pyvmomi + state: latest + +- name: Store the vCenter container IP + set_fact: + vcenter_host: "{{ lookup('env', 'vcenter_host') }}" + +- debug: + var: vcenter_host + +- name: Wait for vcsim controller to come online {{ vcenter_host }} + wait_for: + host: "{{ vcenter_host }}" + port: 5000 + state: started + +- name: Kill vcsim + uri: + url: http://{{ vcenter_host }}:5000/killall + +- name: Start vcsim + uri: + url: http://{{ vcenter_host }}:5000/spawn?cluster=2 + register: vcsim_instance + +- debug: + var: vcsim_instance + +# FIXME: ServerFaultCode: LicenseManager:LicenseManager does not implement: AddLicense +#- name: Add a vCenter evaluation license +# vcenter_license: +# hostname: '{{ vcenter_host }}' +# username: '{{ vcsim_instance["json"]["username"] }}' +# password: '{{ vcsim_instance["json"]["password"] }}' +# validate_certs: no +# license: 00000-00000-00000-00000-00000 +# state: present + +#- name: Remove an (unused) vCenter evaluation license +# vcenter_license: +# hostname: '{{ vcenter_host }}' +# username: '{{ vcsim_instance["json"]["username"] }}' +# password: '{{ vcsim_instance["json"]["password"] }}' +# validate_certs: no +# license: 00000-00000-00000-00000-00000 +# state: absent + +#- name: Add an invalid vCenter license +# vcenter_license: +# hostname: '{{ vcenter_host }}' +# username: '{{ vcsim_instance["json"]["username"] }}' +# password: '{{ vcsim_instance["json"]["password"] }}' +# validate_certs: no +# license: 00000-00000-00000-00000-00001 +# state: present +# ignore_errors: yes