diff --git a/example/.gitkeep b/examples/.gitkeep similarity index 100% rename from example/.gitkeep rename to examples/.gitkeep diff --git a/examples/VINS.yaml b/examples/VINS.yaml new file mode 100644 index 0000000..929bf96 --- /dev/null +++ b/examples/VINS.yaml @@ -0,0 +1,40 @@ +--- +# +# DECORT vins module example +# + +- hosts: localhost + tasks: + - name: obtain JWT + decort_jwt: + oauth2_url: "https://sso.digitalenergy.online" + validity: 1200 + register: my_jwt + delegate_to: localhost + + - name: print out JWT + debug: + var: my_jwt.jwt + delegate_to: localhost + + - name: Manage ViNS at resource group level + decort_vins: + authenticator: jwt + jwt: "{{ my_jwt.jwt }}" + controller_url: "https://ds1.digitalenergy.online" + vins_name: "vins_created_by_decort_VINS_module" + state: present + rg_id: 198 + ext_net_id: -1 + ipcidr: "10.20.30.0/24" + mgmtaddr: "10.20.30.1" + custom_config: false + config_save: false + verify_ssl: false + + register: managed_vins + + - name: print VINS facter + debug: + msg: "{{managed_vins.facts.password}}" + when: managed_vins.facts.password is defined diff --git a/example/affinity.yaml b/examples/affinity.yaml similarity index 100% rename from example/affinity.yaml rename to examples/affinity.yaml diff --git a/examples/annotations.yaml b/examples/annotations.yaml new file mode 100644 index 0000000..6177f74 --- /dev/null +++ b/examples/annotations.yaml @@ -0,0 +1,40 @@ +--- +# +# DECORT k8s module labels, taints, annotations example +# + +- hosts: localhost + tasks: + - name: obtain JWT + decort_jwt: + oauth2_url: "https://sso.digitalenergy.online" + validity: 1200 + register: my_jwt + delegate_to: localhost + + - name: print out JWT + debug: + var: my_jwt.jwt + delegate_to: localhost + + - name: Create k8s cluster + decort_k8s: + authenticator: jwt + jwt: "{{ my_jwt.jwt }}" + controller_url: "https://mr4.digitalenergy.online" + name: "example_kubernetes" + rg_id: 199 + k8ci_id: 4 + state: present + workers: + - name: workgroup1 + labels: + - disktype1=ssd1 + - disktype2=ssd2 + taints: + - key1=value1:NoSchedule + - key2=value2:NoSchedule + annotations: + - node.deckhouse.io/group1=g1 + - node.deckhouse.io/group2=g2 + register: kube diff --git a/example/anti_affinity.yaml b/examples/anti_affinity.yaml similarity index 100% rename from example/anti_affinity.yaml rename to examples/anti_affinity.yaml diff --git a/examples/basicservices.yaml b/examples/basicservices.yaml new file mode 100644 index 0000000..ae9a6b7 --- /dev/null +++ b/examples/basicservices.yaml @@ -0,0 +1,31 @@ +--- +# +# DECORT bservice module example +# + +- hosts: localhost + tasks: + - name: obtain JWT + decort_jwt: + oauth2_url: "https://sso.digitalenergy.online" + validity: 1200 + register: my_jwt + delegate_to: localhost + + - name: print out JWT + debug: + var: my_jwt.jwt + delegate_to: localhost + + - name: Manage bservice at RG + decort_bservice: + account_id: 98 + verify_ssl: false + authenticator: jwt + jwt: "{{ my_jwt.jwt }}" + controller_url: "https://ds1.digitalenergy.online" + rg_id: 1629 + state: present + name: databases + started: True + register: db_bservice diff --git a/example/cloud_init-example.yaml b/examples/cloud-init.yaml similarity index 100% rename from example/cloud_init-example.yaml rename to examples/cloud-init.yaml diff --git a/examples/cloud_init.yaml b/examples/cloud_init.yaml new file mode 100644 index 0000000..4163c0b --- /dev/null +++ b/examples/cloud_init.yaml @@ -0,0 +1,38 @@ +# +# DECORT kvmvm module example +# +- hosts: ansible_master + tasks: + - name: create a VM named cloud-init_example + decort_kvmvm: + annotation: "VM managed by decort_kvmvm module" + authenticator: oauth2 + app_id: "" # Application id from SSO Digital Energy + app_secret: "" # API key from SSO Digital Energy + controller_url: "" #"https://mr4.digitalenergy.online" + name: cloud-init_example + cpu: 2 + ram: 2048 + boot_disk: 10 + image_name: "DECS Ubuntu 18.04 v1.2.3" #Name of OS image + networks: + - type: VINS + id: #VINS id + tags: "Ansible cloud init example" + state: present + rg_id: #Resource group id + ci_user_data: + - packages: + - apache2 + - write_files: + - content: | +
+ Hello World! +
+ owner: user:user + path: /var/www/html/index.html + - hostname: test-apache + - ssh_keys: + - rsa_public: ssh-rsa AAAAOasDmLxnD= user@pc + delegate_to: localhost + register: simple_vm diff --git a/examples/create-osimage.yaml b/examples/create-osimage.yaml new file mode 100644 index 0000000..ae616e4 --- /dev/null +++ b/examples/create-osimage.yaml @@ -0,0 +1,27 @@ +--- +# +# DECORT osimage module example +# +- hosts: localhost + tasks: + - name: create + decort_osimage: + authenticator: oauth2 + verify_ssl: False + controller_url: "https://ds1.digitalenergy.online" + state: present + image_name: "alpine_linux3.14.0" + account_Id: 12345 + url: "https://dl-cdn.alpinelinux.org/alpine/v3.14/releases/x86_64/alpine-virt-3.14.0-x86_64.iso" + boottype: "uefi" + imagetype: "linux" + hotresize: False + image_username: "test" + image_password: "p@ssword" + usernameDL: "testDL" + passwordDL: "p@sswordDL" + architecture: "X86_64" + drivers: "KVM_X86" + + delegate_to: localhost + register: simple_vm diff --git a/examples/create-virtual-osimage.yaml b/examples/create-virtual-osimage.yaml new file mode 100644 index 0000000..015c21e --- /dev/null +++ b/examples/create-virtual-osimage.yaml @@ -0,0 +1,15 @@ +--- +# +# DECORT osimage module example +# +- hosts: localhost + tasks: + - name: create_virtual_osimage + decort_osimage: + authenticator: oauth2 + controller_url: "https://ds1.digitalenergy.online" + image_name: "alpine_linux_3.14.0" + virt_name: "alpine_last" + delegate_to: localhost + register: osimage + diff --git a/examples/get-osimage.yaml b/examples/get-osimage.yaml new file mode 100644 index 0000000..3e61612 --- /dev/null +++ b/examples/get-osimage.yaml @@ -0,0 +1,14 @@ +--- +# +# DECORT osimage module example +# +- hosts: localhost + tasks: + - name: get_osimage + decort_osimage: + authenticator: oauth2 + controller_url: "https://ds1.digitalenergy.online" + image_name: "alpine_linux_3.14.0" + account_Id: 79349 + delegate_to: localhost + register: simple_vm diff --git a/example/kubernetes.yaml b/examples/kubernetes.yaml similarity index 60% rename from example/kubernetes.yaml rename to examples/kubernetes.yaml index 8e73050..065c5f8 100644 --- a/example/kubernetes.yaml +++ b/examples/kubernetes.yaml @@ -6,23 +6,23 @@ tasks: - name: obtain JWT decort_jwt: - oauth2_url: "" #"https://sso.digitalenergy.online" + oauth2_url: "https://sso.digitalenergy.online" validity: 1200 verify_ssl: false register: token delegate_to: localhost - - name: create a VM named cloud-init_example + - name: create a VM named cluster-test decort_k8s: state: present started: True getConfig: True authenticator: jwt jwt: "{{ token.jwt }}" - controller_url: "" #"https://mr4.digitalenergy.online" + controller_url: "https://ds1.digitalenergy.online" name: "cluster-test" - rg_id: # Resource group id - k8ci_id: # k8s ci id + rg_id: 125 + k8ci_id: 18 workers: - name: wg1 ram: 1024 diff --git a/examples/rename-osimage.yaml b/examples/rename-osimage.yaml new file mode 100644 index 0000000..eb13642 --- /dev/null +++ b/examples/rename-osimage.yaml @@ -0,0 +1,15 @@ +--- +# +# DECORT osimage module example +# +- hosts: localhost + tasks: + - name: rename_osimage + decort_osimage: + authenticator: oauth2 + controller_url: "https://ds1.digitalenergy.online" + image_name: "alpine_linux_3.14.0v2.0" + image_id: 54321 + delegate_to: localhost + register: osimage + diff --git a/library/decort_osimage.py b/library/decort_osimage.py index 9c91b30..f09eee7 100644 --- a/library/decort_osimage.py +++ b/library/decort_osimage.py @@ -22,8 +22,7 @@ description: > This module can be used to obtain image ID of an OS image in DECORT cloud to use with subsequent calls to decort_vm module for batch VM provisioning. It will speed up VM creation and save a bunch of extra calls to DECORT cloud controller on each VM creation act. - Note that this module is effectively an information provisioner. It is not designed to and does not manage - nor change state of OS image (or any other) objects in DECORT cloud. + version_added: "2.2" author: - Sergey Shubin @@ -68,8 +67,8 @@ options: image_name: description: - Name of the OS image to use. Module will return the ID of this image. - - 'The specified image name will be looked up in the target DECORT controller and error will be generated if - no matching image is found.' + - 'The specified image name will be looked up in the target DECORT controller and error will be generated + - if no matching image is found.' required: yes jwt: description: @@ -109,10 +108,6 @@ options: - 'This parameter is required when I(authenticator=legacy) and ignored for other authentication modes.' - If not specified in the playbook, the value will be taken from DECORT_USER environment variable. required: no - vdc_id: - description: - - ID of the VDC to limit the search of the OS image to. - required: no verify_ssl: description: - 'Controls SSL verification mode when making API calls to DECORT controller. Set it to False if you @@ -134,19 +129,143 @@ options: - 'This context data is expected to uniquely identify the task carried out by this module invocation so that up-level orchestrator could match returned information to the its internal entities.' required: no + account_name: + description: + - 'Account name. Used to get a unique integer account ID.' + required: no + virt_id: + description: + - 'A unique integer identifier for the virtual image.' + - 'Can be used to obtain information about a virtual image, as well as to create a virtual image and + - bind another operating system image to it.' + required: no + virt_name: + description: + - 'Name of the virtual image. Used to get the `virt_id`, and later information about the virtual image, + - as well as to create a virtual image and bind another operating system image to it.' + required: no + state: + description: + - 'The state of the images. If set to present, operating system images will be created to which + - the account specified in `account_Id` or `account_name` is bound. If set to absent, they will be removed. + required: no + drivers: + description: + - 'A list of compute types (eg virtual servers) that are appropriate for the operating system image. + - Note: `KVM_X86`. Used when creating an operating system image.' + required: no + architecture: + description: + - 'Binary architecture of the image. Note. `X86_64` or `PPC64_LE`. Used when creating + -an operating system image.' + required: no + imagetype: + description: + - 'Image type. `linux`, `windows` or `other`. The default is `linux`. Used when creating + - an operating system image.' + required: no + boottype: + description: + - 'Image upload type. `bios` or `uefi`. The default is `uefi`. Used when creating an operating + -system image.' + required: no + url: + description: + - 'Uniform resource locator (URL) pointing to the iso image of the operating system. Used when + -creating an operating system image.' + required: no + sepId: + description: + - 'The unique integer ID of the storage provider endpoint. Specified in pair with `poolName`. + - Used when creating an operating system image.' + required: no + poolName: + description: + - 'The pool in which the image will be created. Specified in pair with `sepId`. Used when creating + - an operating system image.' + required: no + hotresize: + description: + - 'Whether the image supports "hot" resizing. The default is `false`. Used when creating an operating + - system image.' + required: no + image_username: + description: + - 'An optional username for the image. Used when creating an operating system image.' + required: no + image_password: + description: + - 'An optional password for the image. Used when creating an operating system image. Used when creating + - an operating system image.' + required: no + usernameDL: + description: + - 'The username for loading the binary media. Used in conjunction with `passwordDL`. Used when creating + - an operating system image' + required: no + passwordDL: + description: + - 'The password for loading the binary media. Used in conjunction with `usernameDL`. Used when creating + - an operating system image.' + required: no + permanently: + description: + - 'Whether to permanently delete the image. Used when deleting an image. The default is false.' + required: no + ''' EXAMPLES = ''' -- name: locate OS image specified by its name, store result in image_to_use variable. + - name: create_osimage + decort_osimage: + authenticator: oauth2 + verify_ssl: False + controller_url: "https://ds1.digitalenergy.online" + state: present + image_name: "alpine_linux3.14.0" + account_Id: 12345 + url: "https://dl-cdn.alpinelinux.org/alpine/v3.14/releases/x86_64/alpine-virt-3.14.0-x86_64.iso" + boottype: "uefi" + imagetype: "linux" + hotresize: False + image_username: "test" + image_password: "p@ssw0rd" + usernameDL: "testDL" + passwordDL: "p@ssw0rdDL" + architecture: "X86_64" + drivers: "KVM_X86" + delegate_to: localhost + register: osimage + + - name: get_osimage + decort_osimage: + authenticator: oauth2 + controller_url: "https://ds1.digitalenergy.online" + image_name: "alpine_linux_3.14.0" + account_Id: 12345 + delegate_to: localhost + register: osimage + + - name: create_virtual_osimage + decort_osimage: + authenticator: oauth2 + controller_url: "https://ds1.digitalenergy.online" + image_name: "alpine_linux_3.14.0" + virt_name: "alpine_last" + delegate_to: localhost + register: osimage + + - name: rename_osimage decort_osimage: authenticator: oauth2 - app_id: "{{ MY_APP_ID }}" - app_secret: "{{ MY_APP_SECRET }}" controller_url: "https://ds1.digitalenergy.online" - image_name: "Ubuntu 18.04 v1.2.5" - account_name: "GreyseDevelopment" + image_name: "alpine_linux_3.14.0v2.0" + image_id: 54321 delegate_to: localhost - register: image_to_use + register: osimage + + + ''' RETURN = ''' @@ -157,6 +276,7 @@ facts: sample: facts: id: 100 + linkto: 80 name: "Ubuntu 16.04 v1.0" size: 3 sep_id: 1 @@ -171,94 +291,205 @@ from ansible.module_utils.basic import env_fallback from ansible.module_utils.decort_utils import * +class decort_osimage(DecortController): + def __init__(self,amodule): + super(decort_osimage, self).__init__(amodule) -def decort_osimage_package_facts(arg_osimage_facts, arg_check_mode=False): - """Package a dictionary of OS image according to the decort_osimage module specification. This - dictionary will be returned to the upstream Ansible engine at the completion of the module run. + self.validated_image_id = 0 + self.validated_virt_image_id = 0 + self.validated_image_name = amodule.params['image_name'] + self.validated_virt_image_name = None + self.validated_virt_image_id = amodule.params['virt_id'] + if amodule.params['account_name']: + self.validated_account_id, _ = self.account_find(amodule.params['account_name']) + else: + self.validated_account_id = amodule.params['account_Id'] + + if self.validated_account_id == 0: + # we failed either to find or access the specified account - fail the module + self.result['failed'] = True + self.result['changed'] = False + self.result['msg'] = ("Cannot find account '{}'").format(amodule.params['account_name']) + amodule.fail_json(**self.result) - @param arg_osimage_facts: dictionary with OS image facts as returned by API call to .../images/list - @param arg_check_mode: boolean that tells if this Ansible module is run in check mode. - @return: dictionary with OS image specs populated from arg_osimage_facts. - """ + if amodule.params['image_id'] != 0 and amodule.params['image_name']: + self.validated_image_id = amodule.params['image_id'] + if amodule.params['image_name']: + decort_osimage.decort_image_rename(self,amodule) + self.result['msg'] = ("Image renamed successfully") - ret_dict = dict(id=0, - name="none", - size=0, - type="none", - state="CHECK_MODE", - ) - if arg_check_mode: - # in check mode return immediately with the default values - return ret_dict - if arg_osimage_facts is None: - # if void facts provided - change state value to ABSENT and return - ret_dict['state'] = "ABSENT" - return ret_dict + def decort_image_find(self, amodule): + # function that finds the OS image + image_id, image_facts = self.image_find(image_id=amodule.params['image_id'], image_name=self.validated_image_name, + account_id=self.validated_account_id, rg_id=0, + sepid=amodule.params['sep_id'], + pool=amodule.params['pool']) + return image_id, image_facts + + def decort_virt_image_find(self, amodule): + # function that finds a virtual image + image_id, image_facts = self.virt_image_find(image_id=amodule.params['virt_id'], + account_id=self.validated_account_id, rg_id=0, + sepid=amodule.params['sep_id'], + virt_name=amodule.params['virt_name'], + pool=amodule.params['pool']) + return image_id, image_facts + + + + def decort_image_create(self,amodule): + # function that creates OS image + image_facts = self.image_create(img_name=self.validated_image_name, + url=amodule.params['url'], + gid=amodule.params['gid'], + boottype=amodule.params['boottype'], + imagetype=amodule.params['imagetype'], + hotresize=amodule.params['hotresize'], + username=amodule.params['image_username'], + password=amodule.params['image_password'], + account_Id=amodule.params['account_Id'], + usernameDL=amodule.params['usernameDL'], + passwordDL=amodule.params['passwordDL'], + sepId=amodule.params['sepId'], + poolName=amodule.params['poolName'], + architecture=amodule.params['architecture'], + drivers=amodule.params['drivers']) + self.result['changed'] = True + return image_facts + + def decort_virt_image_link(self,amodule): + # function that links an OS image to a virtual one + self.virt_image_link(imageId=self.validated_virt_image_id, targetId=self.validated_image_id) + image_id, image_facts = decort_osimage.decort_virt_image_find(self, amodule) + self.result['facts'] = decort_osimage.decort_osimage_package_facts(image_facts, amodule.check_mode) + self.result['msg'] = ("Image '{}' linked to virtual image '{}'").format(self.validated_image_id, + decort_osimage.decort_osimage_package_facts(image_facts)['id'],) + return image_id, image_facts - ret_dict['id'] = arg_osimage_facts['id'] - ret_dict['name'] = arg_osimage_facts['name'] - ret_dict['size'] = arg_osimage_facts['size'] - ret_dict['type'] = arg_osimage_facts['type'] - # ret_dict['arch'] = arg_osimage_facts['architecture'] - ret_dict['sep_id'] = arg_osimage_facts['sepId'] - ret_dict['pool'] = arg_osimage_facts['pool'] - ret_dict['state'] = arg_osimage_facts['status'] - - return ret_dict - -def decort_osimage_parameters(): - """Build and return a dictionary of parameters expected by decort_osimage module in a form accepted - by AnsibleModule utility class.""" - - return dict( - app_id=dict(type='str', - required=False, - fallback=(env_fallback, ['DECORT_APP_ID'])), - app_secret=dict(type='str', - required=False, - fallback=(env_fallback, ['DECORT_APP_SECRET']), - no_log=True), - authenticator=dict(type='str', - required=True, - choices=['legacy', 'oauth2', 'jwt']), - controller_url=dict(type='str', required=True), - image_name=dict(type='str', required=True), - jwt=dict(type='str', - required=False, - fallback=(env_fallback, ['DECORT_JWT']), - no_log=True), - oauth2_url=dict(type='str', + def decort_image_delete(self,amodule): + # function that removes an image + self.image_delete(imageId=amodule.image_id_delete, permanently=amodule.params['permanently']) + self.result['changed'] = True + self.result['msg'] = ("Image '{}' deleted").format(amodule.image_id_delete) + + def decort_virt_image_create(self,amodule): + # function that creates a virtual image + image_facts = self.virt_image_create(name=amodule.params['virt_name'], targetId=self.validated_image_id) + image_id, image_facts = decort_osimage.decort_virt_image_find(self, amodule) + self.result['facts'] = decort_osimage.decort_osimage_package_facts(image_facts, amodule.check_mode) + return image_id, image_facts + + def decort_image_rename(self,amodule): + # image renaming function + image_facts = self.image_rename(imageId=self.validated_image_id, name=amodule.params['image_name']) + self.result['msg'] = ("Image renamed successfully") + image_id, image_facts = decort_osimage.decort_image_find(self, amodule) + return image_id, image_facts + + + def decort_osimage_package_facts(arg_osimage_facts, arg_check_mode=False): + """Package a dictionary of OS image according to the decort_osimage module specification. This + dictionary will be returned to the upstream Ansible engine at the completion of the module run. + + @param arg_osimage_facts: dictionary with OS image facts as returned by API call to .../images/list + @param arg_check_mode: boolean that tells if this Ansible module is run in check mode. + + @return: dictionary with OS image specs populated from arg_osimage_facts. + """ + + ret_dict = dict(id=0, + name="none", + size=0, + type="none", + state="CHECK_MODE", ) + + if arg_check_mode: + # in check mode return immediately with the default values + return ret_dict + + if arg_osimage_facts is None: + # if void facts provided - change state value to ABSENT and return + ret_dict['state'] = "ABSENT" + return ret_dict + + ret_dict['id'] = arg_osimage_facts['id'] + ret_dict['name'] = arg_osimage_facts['name'] + ret_dict['size'] = arg_osimage_facts['size'] + ret_dict['type'] = arg_osimage_facts['type'] + # ret_dict['arch'] = arg_osimage_facts['architecture'] + ret_dict['sep_id'] = arg_osimage_facts['sepId'] + ret_dict['pool'] = arg_osimage_facts['pool'] + ret_dict['state'] = arg_osimage_facts['status'] + ret_dict['linkto'] = arg_osimage_facts['linkTo'] + return ret_dict + + + def decort_osimage_parameters(): + """Build and return a dictionary of parameters expected by decort_osimage module in a form accepted + by AnsibleModule utility class.""" + + return dict( + app_id=dict(type='str', required=False, - fallback=(env_fallback, ['DECORT_OAUTH2_URL'])), - password=dict(type='str', + fallback=(env_fallback, ['DECORT_APP_ID'])), + app_secret=dict(type='str', + required=False, + fallback=(env_fallback, ['DECORT_APP_SECRET']), + no_log=True), + authenticator=dict(type='str', + required=True, + choices=['legacy', 'oauth2', 'jwt']), + controller_url=dict(type='str', required=True), + jwt=dict(type='str', + required=False, + fallback=(env_fallback, ['DECORT_JWT']), + no_log=True), + oauth2_url=dict(type='str', + required=False, + fallback=(env_fallback, ['DECORT_OAUTH2_URL'])), + password=dict(type='str', + required=False, + fallback=(env_fallback, ['DECORT_PASSWORD']), + no_log=True), + pool=dict(type='str', required=False, default=""), + sep_id=dict(type='int', required=False, default=0), + account_name=dict(type='str', required=False), + account_Id=dict(type='int', required=False), + user=dict(type='str', required=False, - fallback=(env_fallback, ['DECORT_PASSWORD']), - no_log=True), - pool=dict(type='str', required=False, default=""), - sep_id=dict(type='int', required=False, default=0), - account_name=dict(type='str', required=True), - user=dict(type='str', - required=False, - fallback=(env_fallback, ['DECORT_USER'])), - vdc_id=dict(type='int', required=False, default=0), - verify_ssl=dict(type='bool', required=False, default=True), - workflow_callback=dict(type='str', required=False), - workflow_context=dict(type='str', required=False), - ) - -# Workflow digest: -# 1) authenticate to DECORT controller & validate authentication by issuing API call - done when -# creating DecortController -# 2) obtain a list of OS images accessible to the specified account (and optionally - within -# the specified VDC) -# 3) match specified OS image by its name - if image is not found abort the module -# 5) report result to Ansible + fallback=(env_fallback, ['DECORT_USER'])), + verify_ssl=dict(type='bool', required=False, default=True), + workflow_callback=dict(type='str', required=False), + workflow_context=dict(type='str', required=False), + image_name=dict(type='str', required=False), + image_id=dict(type='int', required=False,default=0), + virt_id=dict(type='int', required=False, default=0), + virt_name=dict(type='str', required=False), + state=dict(type='str', + default='present', + choices=['absent', 'present']), + drivers=dict(type='str', required=False, default="KVM_X86"), + architecture=dict(type='str', required=False, default="X86_64"), + imagetype=dict(type='str', required=False, default="linux"), + boottype=dict(type='str', required=False, default="uefi"), + url=dict(type='str', required=False), + gid=dict(type='int', required=False, default=0), + sepId=dict(type='int', required=False, default=0), + poolName=dict(type='str', required=False), + hotresize=dict(type='bool', required=False, default=False), + image_username=dict(type='str', required=False), + image_password=dict(type='str', required=False), + usernameDL=dict(type='str', required=False), + passwordDL=dict(type='str', required=False), + permanently=dict(type='bool', required=False, default=False), + ) + def main(): - module_parameters = decort_osimage_parameters() + module_parameters = decort_osimage.decort_osimage_parameters() amodule = AnsibleModule(argument_spec=module_parameters, supports_check_mode=True, @@ -273,30 +504,67 @@ def main(): ], ) - decon = DecortController(amodule) + decon = decort_osimage(amodule) + + if amodule.params['image_name'] or amodule.params['image_id']: + image_id, image_facts = decort_osimage.decort_image_find(decon, amodule) + decon.validated_image_id = decort_osimage.decort_osimage_package_facts(image_facts)['id'] + if decort_osimage.decort_osimage_package_facts(image_facts)['id'] > 0: + decon.result['facts'] = decort_osimage.decort_osimage_package_facts(image_facts, amodule.check_mode) + + if amodule.params['state'] == "present" and decon.validated_image_id == 0 and amodule.params['image_name'] and amodule.params['url']: + decort_osimage.decort_image_create(decon,amodule) + decon.result['changed'] = True + image_id, image_facts = decort_osimage.decort_image_find(decon, amodule) + decon.result['msg'] = ("OS image '{}' created").format(decort_osimage.decort_osimage_package_facts(image_facts)['id']) + decon.result['facts'] = decort_osimage.decort_osimage_package_facts(image_facts, amodule.check_mode) + decon.validated_image_id = decort_osimage.decort_osimage_package_facts(image_facts)['id'] + + + elif amodule.params['state'] == "absent" and amodule.params['image_name'] or amodule.params['image_id'] and decort_osimage.decort_osimage_package_facts(image_facts)['accountId'] == amodule.params['account_Id']: + amodule.image_id_delete = decon.validated_image_id + decort_osimage.decort_image_delete(decon,amodule) + + + + if amodule.params['virt_name'] or amodule.params['virt_id']: + + image_id, image_facts = decort_osimage.decort_virt_image_find(decon, amodule) + if decort_osimage.decort_osimage_package_facts(image_facts)['id'] > 0: + decon.result['facts'] = decort_osimage.decort_osimage_package_facts(image_facts, amodule.check_mode) + decon.validated_virt_image_id = decort_osimage.decort_osimage_package_facts(image_facts)['id'] + decon.validated_virt_image_name = decort_osimage.decort_osimage_package_facts(image_facts)['name'] + + + if decort_osimage.decort_osimage_package_facts(image_facts)['id'] == 0 and amodule.params['state'] == "present" and decon.validated_image_id > 0: + image_id, image_facts = decort_osimage.decort_virt_image_create(decon,amodule) + decon.result['msg'] = ("Virtual image '{}' created").format(decort_osimage.decort_osimage_package_facts(image_facts)['id']) + decon.result['changed'] = True + elif decort_osimage.decort_osimage_package_facts(image_facts)['id'] == 0 and amodule.params['state'] == "present" and decon.validated_image_id == 0: + decon.result['msg'] = ("Cannot find OS image") + amodule.fail_json(**decon.result) + + + if decon.validated_image_id: + if decort_osimage.decort_osimage_package_facts(image_facts)['linkto'] != decon.validated_image_id: + decort_osimage.decort_virt_image_link(decon,amodule) + decon.result['changed'] = True + amodule.exit_json(**decon.result) + + + if decon.validated_virt_image_id > 0 and amodule.params['state'] == "absent": + decon.result['msg'] = ("Osimage module cannot delete virtual images.") + decon.result['failed'] = True + amodule.exit_json(**decon.result) - # we need account ID to locate OS images - find the account by the specified name and get its ID - validated_account_id, _ = decon.account_find(amodule.params['account_name']) - if validated_account_id == 0: - # we failed either to find or access the specified account - fail the module - decon.result['failed'] = True - decon.result['changed'] = False - decon.result['msg'] = ("Cannot find account '{}'").format(amodule.params['account_name']) - amodule.fail_json(**decon.result) - image_id, image_facts = decon.image_find(image_id=0, image_name=amodule.params['image_name'], - account_id=validated_account_id, rg_id=0, - sepid=amodule.params['sep_id'], - pool=amodule.params['pool']) if decon.result['failed'] == True: # we failed to find the specified image - fail the module decon.result['changed'] = False amodule.fail_json(**decon.result) - decon.result['facts'] = decort_osimage_package_facts(image_facts, amodule.check_mode) - decon.result['changed'] = False # decort_osimage is a read-only module - make sure the 'changed' flag is set to False amodule.exit_json(**decon.result) - + if __name__ == "__main__": main() diff --git a/module_utils/decort_utils.py b/module_utils/decort_utils.py index e86f6c8..bb378c4 100644 --- a/module_utils/decort_utils.py +++ b/module_utils/decort_utils.py @@ -1247,26 +1247,25 @@ class DecortController(object): def image_find(self, image_id, image_name, account_id, rg_id=0, sepid=0, pool=""): """Locates image specified by name and returns its facts as dictionary. Primary use of this function is to obtain the ID of the image identified by its name and, - optionally SEP ID and/or pool name. Also note that only images in status CREATED are + optionally SEP ID and/or pool name. Also note that only images in status CREATED are returned. @param (string) image_id: ID of the OS image to find. If non-zero ID is specified, then image_name is ignored. @param (string) image_name: name of the OS image to find. This argument is ignored if non-zero image ID is passed. - @param (int) account_id: ID of the account for which the image will be looked up. If set to 0, + @param (int) account_id: ID of the account for which the image will be looked up. If set to 0, the account ID will be obtained from the specified RG ID. @param (int) rg_id: ID of the RG to use as a reference when listing OS images. This argument is ignored if non-zero image id and/or non-zero account_id are specified. - @param (int) sepid: ID of the SEP where the image should be present. If set to 0, there will be no + @param (int) sepid: ID of the SEP where the image should be present. If set to 0, there will be no filtering by SEP ID and the first matching image will be returned. - @param (string) pool: name of the pool where the image should be present. If set to empty string, there + @param (string) pool: name of the pool where the image should be present. If set to empty string, there will be no filtering by pool name and first matching image will be returned. - @return: image ID and dictionary with image specs. If no matching image found, 0 for ID and None for + @return: image ID and dictionary with image specs. If no matching image found, 0 for ID and None for dictionary are returned, and self.result['failed']=True. """ - self.result['waypoints'] = "{} -> {}".format(self.result['waypoints'], "image_find") if image_id > 0: @@ -1292,7 +1291,7 @@ class DecortController(object): for image_record in images_list: if image_record['name'] == image_name and image_record['status'] == "CREATED": if sepid == 0 and pool == "": - # if no filtering by SEP ID or pool name is requested, return the first match + # if no filtering by SEP ID or pool name is requested, return the first match return image_record['id'], image_record # if positive SEP ID and/or non-emtpy pool name are passed, match by them full_match = True @@ -1302,6 +1301,7 @@ class DecortController(object): full_match = False if full_match: return image_record['id'], image_record + self.result['failed'] = False self.result['failed'] = True self.result['msg'] = ("Failed to find OS image by name '{}', SEP ID {}, pool '{}' for " @@ -1310,6 +1310,136 @@ class DecortController(object): account_id) return 0, None + def virt_image_find(self, image_id, virt_name, account_id, rg_id=0, sepid=0, pool=""): + """Locates virtual image specified by name and returns its facts as dictionary. + Primary use of this function is to obtain the ID of the image identified by its name and, + optionally SEP ID and/or pool name. Also note that only virtual images in status CREATED are + returned. + + @param (string) image_id: ID of the OS image to find. If non-zero ID is specified, then + virt_name is ignored. + @param (string) virt_name: name of the OS image to find. This argument is ignored if non-zero + image ID is passed. + @param (int) account_id: ID of the account for which the image will be looked up. If set to 0, + the account ID will be obtained from the specified RG ID. + @param (int) rg_id: ID of the RG to use as a reference when listing OS images. This argument is + ignored if non-zero image id and/or non-zero account_id are specified. + @param (int) sepid: ID of the SEP where the image should be present. If set to 0, there will be no + filtering by SEP ID and the first matching image will be returned. + @param (string) pool: name of the pool where the image should be present. If set to empty string, there + will be no filtering by pool name and first matching image will be returned. + + @return: image ID and dictionary with image specs. If no matching image found, 0 for ID and None for + dictionary are returned, and self.result['failed']=True. + """ + self.result['waypoints'] = "{} -> {}".format(self.result['waypoints'], "virt_image_find") + + + + if image_id > 0: + ret_image_id, ret_image_dict = self._image_get_by_id(image_id) + if (ret_image_id and + (sepid == 0 or sepid == ret_image_dict['sepId']) and + (pool == "" or pool == ret_image_dict['pool'])): + return ret_image_id, ret_image_dict + else: + validated_acc_id = account_id + if account_id == 0: + validated_rg_id, rg_facts = self._rg_get_by_id(rg_id) + if not validated_rg_id: + self.result['failed'] = True + self.result['msg'] = ("Failed to find RG ID {}, and account ID is zero.").format(rg_id) + return 0, None + validated_acc_id = rg_facts['accountId'] + + api_params = dict(accountId=validated_acc_id) + api_resp = self.decort_api_call(requests.post, "/restmachine/cloudapi/image/list", api_params) + # On success the above call will return here. On error it will abort execution by calling fail_json. + images_list = json.loads(api_resp.content.decode('utf8')) + for image_record in images_list: + if image_record['name'] == virt_name and image_record['status'] == "CREATED" and image_record['type'] == "virtual": + if sepid == 0 and pool == "": + # if no filtering by SEP ID or pool name is requested, return the first match + return image_record['id'], image_record + full_match = True + if full_match: + return image_record['id'], image_record + + self.result['failed'] = True + self.result['msg'] = ("Failed to find virtual OS image by name '{}', SEP ID {}, pool '{}' for " + "account ID '{}'.").format(virt_name, + sepid, pool, + account_id) + + return 0, None + + def virt_image_create(self, name, targetId): + + self.result['waypoints'] = "{} -> {}".format(self.result['waypoints'], "virt_image_create") + + api_params = dict(name=name, targetId=targetId,) + api_resp = self.decort_api_call(requests.post, "/restmachine/cloudapi/image/createVirtual", api_params) + # On success the above call will return here. On error it will abort execution by calling fail_json. + virt_image_dict = json.loads(api_resp.content.decode('utf8')) + + self.result['failed'] = False + self.result['changed'] = True + return 0, None + + def image_delete(self, imageId, permanently): + self.result['waypoints'] = "{} -> {}".format(self.result['waypoints'], "image_delete") + + api_params = dict(imageId=imageId, permanently=permanently,) + api_resp = self.decort_api_call(requests.post, "/restmachine/cloudapi/image/delete", api_params) + # On success the above call will return here. On error it will abort execution by calling fail_json. + image_dict = json.loads(api_resp.content.decode('utf8')) + + self.result['changed'] = True + return 0, None + + + def image_create(self,img_name,url,gid,boottype,imagetype,architecture,drivers,hotresize,username,password,account_Id,usernameDL,passwordDL,sepId,poolName): + self.result['waypoints'] = "{} -> {}".format(self.result['waypoints'], "image_create") + + api_params = dict(name=img_name, url=url, + gid=gid, boottype=boottype, + imagetype=imagetype, architecture=architecture, + drivers=drivers, accountId=account_Id, + hotresize=hotresize, username=username, + password=password, usernameDL=usernameDL, + passwordDL=passwordDL, sepId=sepId, + poolName=poolName, + ) + api_resp = self.decort_api_call(requests.post, "/restmachine/cloudapi/image/create", api_params) + # On success the above call will return here. On error it will abort execution by calling fail_json. + virt_image_dict = json.loads(api_resp.content.decode('utf8')) + self.result['failed'] = False + self.result['changed'] = True + return 0, None + + def virt_image_link(self, imageId, targetId): + + self.result['waypoints'] = "{} -> {}".format(self.result['waypoints'], "virt_image_link") + + api_params = dict(imageId=imageId, targetId=targetId,) + api_resp = self.decort_api_call(requests.post, "/restmachine/cloudapi/image/link", api_params) + # On success the above call will return here. On error it will abort execution by calling fail_json. + link_image_dict = json.loads(api_resp.content.decode('utf8')) + self.result['failed'] = False + self.result['changed'] = True + + + return 0, None + + def image_rename(self, imageId, name): + self.result['waypoints'] = "{} -> {}".format(self.result['waypoints'], "image_rename") + api_params = dict(imageId=imageId, name=name,) + api_resp = self.decort_api_call(requests.post, "/restmachine/cloudapi/image/rename", api_params) + # On success the above call will return here. On error it will abort execution by calling fail_json. + link_image_dict = json.loads(api_resp.content.decode('utf8')) + self.result['failed'] = False + self.result['changed'] = True + ################################### # Resource Group (RG) manipulation methods ###################################