diff --git a/lib/ansible/modules/cloud/cloudstack/cs_vpc_offering.py b/lib/ansible/modules/cloud/cloudstack/cs_vpc_offering.py new file mode 100644 index 00000000000..c2e159b637e --- /dev/null +++ b/lib/ansible/modules/cloud/cloudstack/cs_vpc_offering.py @@ -0,0 +1,285 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# Copyright (c) 2017, David Passante (@dpassante) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + +DOCUMENTATION = ''' +--- +module: cs_vpc_offering +short_description: Manages vpc offerings on Apache CloudStack based clouds. +description: + - Create, update, enable, disable and remove CloudStack VPC offerings. +version_added: '2.5' +author: "David Passante (@dpassante)" +options: + name: + description: + - The name of the vpc offering + required: true + state: + description: + - State of the vpc offering. + choices: [ enabled, present, disabled, absent ] + required: false + default: present + display_text: + description: + - Display text of the vpc offerings + required: false + service_capabilities: + description: + - Desired service capabilities as part of vpc offering. + aliases: [ service_capability ] + service_offering: + description: + - The name or ID of the service offering for the VPC router appliance. + required: false + supported_services: + description: + - Services supported by the vpc offering + aliases: [ supported_service ] + required: false + service_providers: + description: + - provider to service mapping. If not specified, the provider for the service will be mapped to the default provider on the physical network + aliases: [ service_provider ] + required: false + poll_async: + description: + - Poll async jobs until job has finished. + default: true +extends_documentation_fragment: cloudstack +''' + +EXAMPLES = ''' +# Create a vpc offering and enable it +- local_action: + module: cs_vpc_offering + name: "my_vpc_offering" + display_text: "vpc offering description" + state: enabled + supported_services: [ Dns, Dhcp ] + service_providers: + - {service: 'dns', provider: 'virtualrouter'} + - {service: 'dhcp', provider: 'virtualrouter'} + +# Remove a vpc offering +- local_action: + module: cs_vpc_offering + name: "my_vpc_offering" + state: absent +''' + +RETURN = ''' +--- +id: + description: UUID of the vpc offering. + returned: success + type: string + sample: a6f7a5fc-43f8-11e5-a151-feff819cdc9f +name: + description: The name of the vpc offering + returned: success + type: string + sample: MyCustomVPCOffering +display_text: + description: The display text of the vpc offering + returned: success + type: string + sample: My vpc offering +state: + description: The state of the vpc offering + returned: success + type: string + sample: Enabled +service_offering_id: + description: The service offering ID. + returned: success + type: string + sample: c5f7a5fc-43f8-11e5-a151-feff819cdc9f +is_default: + description: Whether VPC offering is the default offering or not. + returned: success + type: bool + sample: false +region_level: + description: Indicated if the offering can support region level vpc. + returned: success + type: bool + sample: false +distributed: + description: Indicates if the vpc offering supports distributed router for one-hop forwarding. + returned: success + type: bool + sample: false +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.cloudstack import ( + AnsibleCloudStack, + cs_argument_spec, + cs_required_together, +) + + +class AnsibleCloudStackVPCOffering(AnsibleCloudStack): + + def __init__(self, module): + super(AnsibleCloudStackVPCOffering, self).__init__(module) + self.returns = { + 'serviceofferingid': 'service_offering_id', + 'isdefault': 'is_default', + 'distributedvpcrouter': 'distributed', + 'supportsregionLevelvpc': 'region_level', + } + self.vpc_offering = None + + def get_vpc_offering(self): + if self.vpc_offering: + return self.vpc_offering + + args = { + 'name': self.module.params.get('name'), + } + vo = self.query_api('listVPCOfferings', **args) + + if vo: + self.vpc_offering = vo['vpcoffering'][0] + + return self.vpc_offering + + def get_service_offering_id(self): + service_offering = self.module.params.get('service_offering') + if not service_offering: + return None + + args = { + 'issystem': True + } + + service_offerings = self.query_api('listServiceOfferings', **args) + if service_offerings: + for s in service_offerings['serviceoffering']: + if service_offering in [s['name'], s['id']]: + return s['id'] + self.fail_json(msg="Service offering '%s' not found" % service_offering) + + def create_or_update(self): + vpc_offering = self.get_vpc_offering() + + if not vpc_offering: + vpc_offering = self.create_vpc_offering() + + return self.update_vpc_offering(vpc_offering) + + def create_vpc_offering(self): + vpc_offering = None + self.result['changed'] = True + args = { + 'name': self.module.params.get('name'), + 'state': self.module.params.get('state'), + 'displaytext': self.module.params.get('display_text'), + 'supportedservices': self.module.params.get('supported_services'), + 'serviceproviderlist': self.module.params.get('service_providers'), + 'serviceofferingid': self.get_service_offering_id(), + } + + required_params = [ + 'display_text', + 'supported_services', + ] + self.module.fail_on_missing_params(required_params=required_params) + + if not self.module.check_mode: + res = self.query_api('createVPCOffering', **args) + poll_async = self.module.params.get('poll_async') + if poll_async: + vpc_offering = self.poll_job(res, 'vpcoffering') + + return vpc_offering + + def delete_vpc_offering(self): + vpc_offering = self.get_vpc_offering() + + if vpc_offering: + self.result['changed'] = True + + args = { + 'id': vpc_offering['id'], + } + + if not self.module.check_mode: + res = self.query_api('deleteVPCOffering', **args) + poll_async = self.module.params.get('poll_async') + if poll_async: + vpc_offering = self.poll_job(res, 'vpcoffering') + + return vpc_offering + + def update_vpc_offering(self, vpc_offering): + if not vpc_offering: + return vpc_offering + + args = { + 'id': vpc_offering['id'], + 'state': self.module.params.get('state'), + 'name': self.module.params.get('name'), + 'displaytext': self.module.params.get('display_text'), + } + + if args['state'] in ['enabled', 'disabled']: + args['state'] = args['state'].title() + else: + del args['state'] + + if self.has_changed(args, vpc_offering): + self.result['changed'] = True + + if not self.module.check_mode: + res = self.query_api('updateVPCOffering', **args) + poll_async = self.module.params.get('poll_async') + if poll_async: + vpc_offering = self.poll_job(res, 'vpcoffering') + + return vpc_offering + + +def main(): + argument_spec = cs_argument_spec() + argument_spec.update(dict( + name=dict(required=True), + display_text=dict(), + state=dict(choices=['enabled', 'present', 'disabled', 'absent'], default='present'), + service_capabilities=dict(type='list', aliases=['service_capability']), + service_offering=dict(), + supported_services=dict(type='list', aliases=['supported_service']), + service_providers=dict(type='list', aliases=['service_provider']), + poll_async=dict(type='bool', default=True), + )) + + module = AnsibleModule( + argument_spec=argument_spec, + required_together=cs_required_together(), + supports_check_mode=True + ) + + acs_vpc_offering = AnsibleCloudStackVPCOffering(module) + + state = module.params.get('state') + if state in ['absent']: + vpc_offering = acs_vpc_offering.delete_vpc_offering() + else: + vpc_offering = acs_vpc_offering.create_or_update() + + result = acs_vpc_offering.get_result(vpc_offering) + + module.exit_json(**result) + + +if __name__ == '__main__': + main() diff --git a/test/integration/targets/cs_vpc_offering/aliases b/test/integration/targets/cs_vpc_offering/aliases new file mode 100644 index 00000000000..ee8454c6d12 --- /dev/null +++ b/test/integration/targets/cs_vpc_offering/aliases @@ -0,0 +1,2 @@ +cloud/cs +posix/ci/cloud/group1/cs diff --git a/test/integration/targets/cs_vpc_offering/meta/main.yml b/test/integration/targets/cs_vpc_offering/meta/main.yml new file mode 100644 index 00000000000..e9a5b9eeaef --- /dev/null +++ b/test/integration/targets/cs_vpc_offering/meta/main.yml @@ -0,0 +1,3 @@ +--- +dependencies: + - cs_common diff --git a/test/integration/targets/cs_vpc_offering/tasks/main.yml b/test/integration/targets/cs_vpc_offering/tasks/main.yml new file mode 100644 index 00000000000..0336495ff19 --- /dev/null +++ b/test/integration/targets/cs_vpc_offering/tasks/main.yml @@ -0,0 +1,392 @@ +--- +- name: setup + cs_vpc_offering: + name: "{{ cs_resource_prefix }}_vpc" + state: absent + register: vpcoffer +- name: verify setup + assert: + that: + - vpcoffer is successful + +- name: test fail if missing name + cs_vpc_offering: + register: vpcoffer + ignore_errors: true +- name: verify results of fail if missing name + assert: + that: + - vpcoffer is failed + - 'vpcoffer.msg == "missing required arguments: name"' + +- name: test fail if missing params + cs_vpc_offering: + name: "{{ cs_resource_prefix }}_vpc" + register: vpcoffer + ignore_errors: true +- name: verify results of fail if missing params + assert: + that: + - vpcoffer is failed + - 'vpcoffer.msg == "missing required arguments: display_text, supported_services"' + +- name: test create vpc offer in check mode + cs_vpc_offering: + name: "{{ cs_resource_prefix }}_vpc" + display_text: "vpc offering description" + supported_services: [ Dns, PortForwarding, Dhcp, SourceNat, UserData, StaticNat, Vpn, Lb ] + service_providers: + - { service: 'dns', provider: 'virtualrouter' } + - { service: 'dhcp', provider: 'virtualrouter' } + register: vpcoffer + check_mode: yes +- name: verify results of vpc offer in check mode + assert: + that: + - vpcoffer is successful + - vpcoffer is changed + +- name: test create vpc offer + cs_vpc_offering: + name: "{{ cs_resource_prefix }}_vpc" + display_text: "vpc offering description" + supported_services: [ Dns, PortForwarding, Dhcp, SourceNat, UserData, StaticNat, Vpn, Lb ] + service_providers: + - { service: 'dns', provider: 'virtualrouter' } + - { service: 'dhcp', provider: 'virtualrouter' } + register: vpcoffer +- name: verify results of vpc offer + assert: + that: + - vpcoffer is successful + - vpcoffer is changed + - vpcoffer.name == "{{ cs_resource_prefix }}_vpc" + - vpcoffer.state == "Disabled" + - vpcoffer.display_text == "vpc offering description" + - vpcoffer.distributed == false + - vpcoffer.region_level == false + +- name: test create vpc offer idempotence + cs_vpc_offering: + name: "{{ cs_resource_prefix }}_vpc" + display_text: "vpc offering description" + supported_services: [ Dns, PortForwarding, Dhcp, SourceNat, UserData, StaticNat, Vpn, Lb ] + service_providers: + - { service: 'dns', provider: 'virtualrouter' } + - { service: 'dhcp', provider: 'virtualrouter' } + register: vpcoffer +- name: verify results of create vpc offer idempotence + assert: + that: + - vpcoffer is successful + - vpcoffer is not changed + - vpcoffer.name == "{{ cs_resource_prefix }}_vpc" + - vpcoffer.state == "Disabled" + - vpcoffer.display_text == "vpc offering description" + - vpcoffer.distributed == false + - vpcoffer.region_level == false + +- name: test enabling existing vpc offer in check_mode + cs_vpc_offering: + name: "{{ cs_resource_prefix }}_vpc" + state: enabled + register: vpcoffer + check_mode: yes +- name: verify results of enabling existing vpc offer in check_mode + assert: + that: + - vpcoffer is successful + - vpcoffer is changed + - vpcoffer.name == "{{ cs_resource_prefix }}_vpc" + - vpcoffer.state == "Disabled" + - vpcoffer.display_text == "vpc offering description" + +- name: test enabling existing vpc offer + cs_vpc_offering: + name: "{{ cs_resource_prefix }}_vpc" + state: enabled + register: vpcoffer +- name: verify results of enabling existing vpc offer + assert: + that: + - vpcoffer is successful + - vpcoffer is changed + - vpcoffer.name == "{{ cs_resource_prefix }}_vpc" + - vpcoffer.state == "Enabled" + - vpcoffer.display_text == "vpc offering description" + +- name: test enabling existing vpc offer idempotence + cs_vpc_offering: + name: "{{ cs_resource_prefix }}_vpc" + state: enabled + register: vpcoffer +- name: verify results of enabling existing vpc idempotence + assert: + that: + - vpcoffer is successful + - vpcoffer is not changed + - vpcoffer.name == "{{ cs_resource_prefix }}_vpc" + - vpcoffer.state == "Enabled" + - vpcoffer.display_text == "vpc offering description" + +- name: test disabling vpc offer in check_mode + cs_vpc_offering: + name: "{{ cs_resource_prefix }}_vpc" + display_text: "vpc offering description" + supported_services: [ Dns, PortForwarding, Dhcp, SourceNat, UserData, StaticNat, Vpn, Lb ] + service_providers: + - { service: 'dns', provider: 'virtualrouter' } + - { service: 'dhcp', provider: 'virtualrouter' } + state: disabled + register: vpcoffer + check_mode: yes +- name: verify results of disabling vpc offer in check_mode + assert: + that: + - vpcoffer is successful + - vpcoffer is changed + - vpcoffer.name == "{{ cs_resource_prefix }}_vpc" + - vpcoffer.state == "Enabled" + - vpcoffer.display_text == "vpc offering description" + +- name: test disabling vpc offer + cs_vpc_offering: + name: "{{ cs_resource_prefix }}_vpc" + display_text: "vpc offering description" + supported_services: [ Dns, PortForwarding, Dhcp, SourceNat, UserData, StaticNat, Vpn, Lb ] + service_providers: + - { service: 'dns', provider: 'virtualrouter' } + - { service: 'dhcp', provider: 'virtualrouter' } + state: disabled + register: vpcoffer +- name: verify results of disabling vpc offer + assert: + that: + - vpcoffer is successful + - vpcoffer is changed + - vpcoffer.name == "{{ cs_resource_prefix }}_vpc" + - vpcoffer.state == "Disabled" + - vpcoffer.display_text == "vpc offering description" + +- name: test disabling vpc offer idempotence + cs_vpc_offering: + name: "{{ cs_resource_prefix }}_vpc" + display_text: "vpc offering description" + supported_services: [ Dns, PortForwarding, Dhcp, SourceNat, UserData, StaticNat, Vpn, Lb ] + service_providers: + - { service: 'dns', provider: 'virtualrouter' } + - { service: 'dhcp', provider: 'virtualrouter' } + state: disabled + register: vpcoffer +- name: verify results of disabling vpc idempotence + assert: + that: + - vpcoffer is successful + - vpcoffer is not changed + - vpcoffer.name == "{{ cs_resource_prefix }}_vpc" + - vpcoffer.state == "Disabled" + - vpcoffer.display_text == "vpc offering description" + +- name: test rename vpc offer in check_mode + cs_vpc_offering: + name: "{{ cs_resource_prefix }}_vpc" + display_text: "vpc offering description renamed" + supported_services: [ Dns, PortForwarding, Dhcp, SourceNat, UserData, StaticNat, Vpn, Lb ] + service_providers: + - { service: 'dns', provider: 'virtualrouter' } + - { service: 'dhcp', provider: 'virtualrouter' } + state: disabled + register: vpcoffer + check_mode: yes +- name: verify results of rename vpc offer in check_mode + assert: + that: + - vpcoffer is successful + - vpcoffer is changed + - vpcoffer.name == "{{ cs_resource_prefix }}_vpc" + - vpcoffer.state == "Disabled" + - vpcoffer.display_text == "vpc offering description" + +- name: test rename vpc offer + cs_vpc_offering: + name: "{{ cs_resource_prefix }}_vpc" + display_text: "vpc offering description renamed" + supported_services: [ Dns, PortForwarding, Dhcp, SourceNat, UserData, StaticNat, Vpn, Lb ] + service_providers: + - { service: 'dns', provider: 'virtualrouter' } + - { service: 'dhcp', provider: 'virtualrouter' } + state: disabled + register: vpcoffer +- name: verify results of rename vpc offer + assert: + that: + - vpcoffer is successful + - vpcoffer is changed + - vpcoffer.name == "{{ cs_resource_prefix }}_vpc" + - vpcoffer.state == "Disabled" + - vpcoffer.display_text == "vpc offering description renamed" + +- name: test rename vpc offer idempotence + cs_vpc_offering: + name: "{{ cs_resource_prefix }}_vpc" + display_text: "vpc offering description renamed" + supported_services: [ Dns, PortForwarding, Dhcp, SourceNat, UserData, StaticNat, Vpn, Lb ] + service_providers: + - { service: 'dns', provider: 'virtualrouter' } + - { service: 'dhcp', provider: 'virtualrouter' } + state: disabled + register: vpcoffer +- name: verify results of rename vpc offer idempotence + assert: + that: + - vpcoffer is successful + - vpcoffer is not changed + - vpcoffer.name == "{{ cs_resource_prefix }}_vpc" + - vpcoffer.state == "Disabled" + - vpcoffer.display_text == "vpc offering description renamed" + +- name: test update offer with minimal params in check_mode + cs_vpc_offering: + name: "{{ cs_resource_prefix }}_vpc" + display_text: "vpc offering description update" + register: vpcoffer + check_mode: yes +- name: verify results of update offer with minimal params in check_mode + assert: + that: + - vpcoffer is successful + - vpcoffer is changed + - vpcoffer.name == "{{ cs_resource_prefix }}_vpc" + - vpcoffer.state == "Disabled" + - vpcoffer.display_text == "vpc offering description renamed" + +- name: test update offer with minimal params + cs_vpc_offering: + name: "{{ cs_resource_prefix }}_vpc" + display_text: "vpc offering description update" + register: vpcoffer +- name: verify results of update offer with minimal params + assert: + that: + - vpcoffer is successful + - vpcoffer is changed + - vpcoffer.name == "{{ cs_resource_prefix }}_vpc" + - vpcoffer.state == "Disabled" + - vpcoffer.display_text == "vpc offering description update" + +- name: test update offer with minimal params idempotency + cs_vpc_offering: + name: "{{ cs_resource_prefix }}_vpc" + display_text: "vpc offering description update" + register: vpcoffer +- name: verify results of update offer with minimal params idempotency + assert: + that: + - vpcoffer is successful + - vpcoffer is not changed + - vpcoffer.name == "{{ cs_resource_prefix }}_vpc" + - vpcoffer.state == "Disabled" + - vpcoffer.display_text == "vpc offering description update" + +- name: test remove vpc offer in check_mode + cs_vpc_offering: + name: "{{ cs_resource_prefix }}_vpc" + state: absent + register: vpcoffer + check_mode: yes +- name: verify results of rename vpc offer in check_mode + assert: + that: + - vpcoffer is successful + - vpcoffer is changed + - vpcoffer.name == "{{ cs_resource_prefix }}_vpc" + - vpcoffer.state == "Disabled" + - vpcoffer.display_text == "vpc offering description update" + +- name: test remove vpc offer + cs_vpc_offering: + name: "{{ cs_resource_prefix }}_vpc" + state: absent + register: vpcoffer +- name: verify results of rename vpc offer + assert: + that: + - vpcoffer is successful + - vpcoffer is changed + +- name: test remove vpc offer idempotence + cs_vpc_offering: + name: "{{ cs_resource_prefix }}_vpc" + state: absent + register: vpcoffer +- name: verify results of rename vpc offer idempotence + assert: + that: + - vpcoffer is successful + - vpcoffer is not changed + +- name: test create enabled vpc offer in check mode + cs_vpc_offering: + name: "{{ cs_resource_prefix }}_vpc" + display_text: "vpc offering description" + supported_services: [ Dns, PortForwarding, Dhcp, SourceNat, UserData, StaticNat, Vpn, Lb ] + service_providers: + - { service: 'dns', provider: 'virtualrouter' } + - { service: 'dhcp', provider: 'virtualrouter' } + state: enabled + register: vpcoffer + check_mode: yes +- name: verify results of create enabled vpc offer in check mode + assert: + that: + - vpcoffer is successful + - vpcoffer is changed + +- name: test create enabled vpc offer + cs_vpc_offering: + name: "{{ cs_resource_prefix }}_vpc" + display_text: "vpc offering description" + supported_services: [ Dns, PortForwarding, Dhcp, SourceNat, UserData, StaticNat, Vpn, Lb ] + service_providers: + - { service: 'dns', provider: 'virtualrouter' } + - { service: 'dhcp', provider: 'virtualrouter' } + state: enabled + register: vpcoffer +- name: verify results of create enabled vpc offer + assert: + that: + - vpcoffer is successful + - vpcoffer is changed + - vpcoffer.name == "{{ cs_resource_prefix }}_vpc" + - vpcoffer.state == "Enabled" + - vpcoffer.display_text == "vpc offering description" + +- name: test create enabled vpc offer idempotence + cs_vpc_offering: + name: "{{ cs_resource_prefix }}_vpc" + display_text: "vpc offering description" + supported_services: [ Dns, PortForwarding, Dhcp, SourceNat, UserData, StaticNat, Vpn, Lb ] + service_providers: + - { service: 'dns', provider: 'virtualrouter' } + - { service: 'dhcp', provider: 'virtualrouter' } + state: enabled + register: vpcoffer +- name: verify results of create enabled vpc offer idempotence + assert: + that: + - vpcoffer is successful + - vpcoffer is not changed + - vpcoffer.name == "{{ cs_resource_prefix }}_vpc" + - vpcoffer.state == "Enabled" + - vpcoffer.display_text == "vpc offering description" + +- name: remove vpc offer + cs_vpc_offering: + name: "{{ cs_resource_prefix }}_vpc" + state: absent + register: vpcoffer +- name: verify results of remove vpc offer + assert: + that: + - vpcoffer is successful + - vpcoffer is changed