def register(profile, name, url, custom_data): # The API client # While to config file that we are parsing contains multiple regions, we want to use 1 exact match. # We select this by passing it's name to the read_config function. cs = CloudStack(**read_config(ini_group=profile)) # To register a template we need to have an `ostypeid`, so we resolve that here # All templates are treated equally and use the same type so we just do a static lookup. ostype = cs.listOsTypes(description="Other PV Virtio-SCSI (64-bit)") ostype = ostype['ostype'][0]['id'] # Prepare template tags # With this we can look up the template without depending on the ID. # custom_data is a base64 encoded json, so we first need to decode that. data = json.loads(base64.b64decode(custom_data)) tags = [] for k, v in data.items(): item = {"key": k, "value": v} tags.append(item) # Register the template and keep it's ID so we can find it later template = cs.registerTemplate(format="qcow2", hypervisor="kvm", isdynamicallyscalable="true", isextractable="true", passwordEnabled="true", isfeatured="true", ispublic="true", zoneids="-1", ostypeid=ostype, displaytext=name, name=name, url=url) template = template['template'][0]['id'] # Attach tags to template cs.createTags( resourceids=template, resourcetype="Template", tags=tags, )
class AnsibleCloudStack(object): def __init__(self, module): if not has_lib_cs: module.fail_json(msg="python library cs required: pip install cs") self.result = { 'changed': False, 'diff' : { 'before': dict(), 'after': dict() } } # Common returns, will be merged with self.returns # search_for_key: replace_with_key self.common_returns = { 'id': 'id', 'name': 'name', 'created': 'created', 'zonename': 'zone', 'state': 'state', 'project': 'project', 'account': 'account', 'domain': 'domain', 'displaytext': 'display_text', 'displayname': 'display_name', 'description': 'description', } # Init returns dict for use in subclasses self.returns = {} # these values will be casted to int self.returns_to_int = {} # these keys will be compared case sensitive in self.has_changed() self.case_sensitive_keys = [ 'id', 'displaytext', 'displayname', 'description', ] self.module = module self._connect() # Helper for VPCs self._vpc_networks_ids = None self.domain = None self.account = None self.project = None self.ip_address = None self.network = None self.vpc = None self.zone = None self.vm = None self.vm_default_nic = None self.os_type = None self.hypervisor = None self.capabilities = None def _connect(self): api_key = self.module.params.get('api_key') api_secret = self.module.params.get('api_secret') api_url = self.module.params.get('api_url') api_http_method = self.module.params.get('api_http_method') api_timeout = self.module.params.get('api_timeout') if api_key and api_secret and api_url: self.cs = CloudStack( endpoint=api_url, key=api_key, secret=api_secret, timeout=api_timeout, method=api_http_method ) else: api_region = self.module.params.get('api_region', 'cloudstack') self.cs = CloudStack(**read_config(api_region)) def get_or_fallback(self, key=None, fallback_key=None): value = self.module.params.get(key) if not value: value = self.module.params.get(fallback_key) return value # TODO: for backward compatibility only, remove if not used anymore def _has_changed(self, want_dict, current_dict, only_keys=None): return self.has_changed(want_dict=want_dict, current_dict=current_dict, only_keys=only_keys) def has_changed(self, want_dict, current_dict, only_keys=None): result = False for key, value in want_dict.iteritems(): # Optionally limit by a list of keys if only_keys and key not in only_keys: continue # Skip None values if value is None: continue if key in current_dict: if isinstance(value, (int, float, long, complex)): # ensure we compare the same type if isinstance(value, int): current_dict[key] = int(current_dict[key]) elif isinstance(value, float): current_dict[key] = float(current_dict[key]) elif isinstance(value, long): current_dict[key] = long(current_dict[key]) elif isinstance(value, complex): current_dict[key] = complex(current_dict[key]) if value != current_dict[key]: self.result['diff']['before'][key] = current_dict[key] self.result['diff']['after'][key] = value result = True else: if self.case_sensitive_keys and key in self.case_sensitive_keys: if value != current_dict[key].encode('utf-8'): self.result['diff']['before'][key] = current_dict[key].encode('utf-8') self.result['diff']['after'][key] = value result = True # Test for diff in case insensitive way elif value.lower() != current_dict[key].encode('utf-8').lower(): self.result['diff']['before'][key] = current_dict[key].encode('utf-8') self.result['diff']['after'][key] = value result = True else: self.result['diff']['before'][key] = None self.result['diff']['after'][key] = value result = True return result def _get_by_key(self, key=None, my_dict=None): if my_dict is None: my_dict = {} if key: if key in my_dict: return my_dict[key] self.module.fail_json(msg="Something went wrong: %s not found" % key) return my_dict def get_vpc(self, key=None): """Return a VPC dictionary or the value of given key of.""" if self.vpc: return self._get_by_key(key, self.vpc) vpc = self.module.params.get('vpc') if not vpc: vpc = os.environ.get('CLOUDSTACK_VPC') if not vpc: return None args = { 'account': self.get_account(key='name'), 'domainid': self.get_domain(key='id'), 'projectid': self.get_project(key='id'), 'zoneid': self.get_zone(key='id'), } vpcs = self.cs.listVPCs(**args) if not vpcs: self.module.fail_json(msg="No VPCs available.") for v in vpcs['vpc']: if vpc in [v['displaytext'], v['name'], v['id']]: self.vpc = v return self._get_by_key(key, self.vpc) self.module.fail_json(msg="VPC '%s' not found" % vpc) def is_vm_in_vpc(self, vm): for n in vm.get('nic'): if n.get('isdefault', False): return self.is_vpc_network(network_id=n['networkid']) self.module.fail_json(msg="VM has no default nic") def is_vpc_network(self, network_id): """Returns True if network is in VPC.""" # This is an efficient way to query a lot of networks at a time if self._vpc_networks_ids is None: args = { 'account': self.get_account(key='name'), 'domainid': self.get_domain(key='id'), 'projectid': self.get_project(key='id'), 'zoneid': self.get_zone(key='id'), } vpcs = self.cs.listVPCs(**args) self._vpc_networks_ids = [] if vpcs: for vpc in vpcs['vpc']: for n in vpc.get('network',[]): self._vpc_networks_ids.append(n['id']) return network_id in self._vpc_networks_ids def get_network(self, key=None): """Return a network dictionary or the value of given key of.""" if self.network: return self._get_by_key(key, self.network) network = self.module.params.get('network') if not network: return None args = { 'account': self.get_account(key='name'), 'domainid': self.get_domain(key='id'), 'projectid': self.get_project(key='id'), 'zoneid': self.get_zone(key='id'), 'vpcid': self.get_vpc(key='id') } networks = self.cs.listNetworks(**args) if not networks: self.module.fail_json(msg="No networks available.") for n in networks['network']: # ignore any VPC network if vpc param is not given if 'vpcid' in n and not self.get_vpc(key='id'): continue if network in [n['displaytext'], n['name'], n['id']]: self.network = n return self._get_by_key(key, self.network) self.module.fail_json(msg="Network '%s' not found" % network) def get_project(self, key=None): if self.project: return self._get_by_key(key, self.project) project = self.module.params.get('project') if not project: project = os.environ.get('CLOUDSTACK_PROJECT') if not project: return None args = {} args['account'] = self.get_account(key='name') args['domainid'] = self.get_domain(key='id') projects = self.cs.listProjects(**args) if projects: for p in projects['project']: if project.lower() in [ p['name'].lower(), p['id'] ]: self.project = p return self._get_by_key(key, self.project) self.module.fail_json(msg="project '%s' not found" % project) def get_ip_address(self, key=None): if self.ip_address: return self._get_by_key(key, self.ip_address) ip_address = self.module.params.get('ip_address') if not ip_address: self.module.fail_json(msg="IP address param 'ip_address' is required") args = { 'ipaddress': ip_address, 'account': self.get_account(key='name'), 'domainid': self.get_domain(key='id'), 'projectid': self.get_project(key='id'), 'vpcid': self.get_vpc(key='id'), } ip_addresses = self.cs.listPublicIpAddresses(**args) if not ip_addresses: self.module.fail_json(msg="IP address '%s' not found" % args['ipaddress']) self.ip_address = ip_addresses['publicipaddress'][0] return self._get_by_key(key, self.ip_address) def get_vm_guest_ip(self): vm_guest_ip = self.module.params.get('vm_guest_ip') default_nic = self.get_vm_default_nic() if not vm_guest_ip: return default_nic['ipaddress'] for secondary_ip in default_nic['secondaryip']: if vm_guest_ip == secondary_ip['ipaddress']: return vm_guest_ip self.module.fail_json(msg="Secondary IP '%s' not assigned to VM" % vm_guest_ip) def get_vm_default_nic(self): if self.vm_default_nic: return self.vm_default_nic nics = self.cs.listNics(virtualmachineid=self.get_vm(key='id')) if nics: for n in nics['nic']: if n['isdefault']: self.vm_default_nic = n return self.vm_default_nic self.module.fail_json(msg="No default IP address of VM '%s' found" % self.module.params.get('vm')) def get_vm(self, key=None): if self.vm: return self._get_by_key(key, self.vm) vm = self.module.params.get('vm') if not vm: self.module.fail_json(msg="Virtual machine param 'vm' is required") vpc_id = self.get_vpc(key='id') args = { 'account': self.get_account(key='name'), 'domainid': self.get_domain(key='id'), 'projectid': self.get_project(key='id'), 'zoneid': self.get_zone(key='id'), 'vpcid': vpc_id, } vms = self.cs.listVirtualMachines(**args) if vms: for v in vms['virtualmachine']: # Due the limitation of the API, there is no easy way (yet) to get only those VMs # not belonging to a VPC. if not vpc_id and self.is_vm_in_vpc(vm=v): continue if vm.lower() in [ v['name'].lower(), v['displayname'].lower(), v['id'] ]: self.vm = v return self._get_by_key(key, self.vm) self.module.fail_json(msg="Virtual machine '%s' not found" % vm) def get_zone(self, key=None): if self.zone: return self._get_by_key(key, self.zone) zone = self.module.params.get('zone') if not zone: zone = os.environ.get('CLOUDSTACK_ZONE') zones = self.cs.listZones() # use the first zone if no zone param given if not zone: self.zone = zones['zone'][0] return self._get_by_key(key, self.zone) if zones: for z in zones['zone']: if zone.lower() in [ z['name'].lower(), z['id'] ]: self.zone = z return self._get_by_key(key, self.zone) self.module.fail_json(msg="zone '%s' not found" % zone) def get_os_type(self, key=None): if self.os_type: return self._get_by_key(key, self.zone) os_type = self.module.params.get('os_type') if not os_type: return None os_types = self.cs.listOsTypes() if os_types: for o in os_types['ostype']: if os_type in [ o['description'], o['id'] ]: self.os_type = o return self._get_by_key(key, self.os_type) self.module.fail_json(msg="OS type '%s' not found" % os_type) def get_hypervisor(self): if self.hypervisor: return self.hypervisor hypervisor = self.module.params.get('hypervisor') hypervisors = self.cs.listHypervisors() # use the first hypervisor if no hypervisor param given if not hypervisor: self.hypervisor = hypervisors['hypervisor'][0]['name'] return self.hypervisor for h in hypervisors['hypervisor']: if hypervisor.lower() == h['name'].lower(): self.hypervisor = h['name'] return self.hypervisor self.module.fail_json(msg="Hypervisor '%s' not found" % hypervisor) def get_account(self, key=None): if self.account: return self._get_by_key(key, self.account) account = self.module.params.get('account') if not account: account = os.environ.get('CLOUDSTACK_ACCOUNT') if not account: return None domain = self.module.params.get('domain') if not domain: self.module.fail_json(msg="Account must be specified with Domain") args = {} args['name'] = account args['domainid'] = self.get_domain(key='id') args['listall'] = True accounts = self.cs.listAccounts(**args) if accounts: self.account = accounts['account'][0] return self._get_by_key(key, self.account) self.module.fail_json(msg="Account '%s' not found" % account) def get_domain(self, key=None): if self.domain: return self._get_by_key(key, self.domain) domain = self.module.params.get('domain') if not domain: domain = os.environ.get('CLOUDSTACK_DOMAIN') if not domain: return None args = {} args['listall'] = True domains = self.cs.listDomains(**args) if domains: for d in domains['domain']: if d['path'].lower() in [ domain.lower(), "root/" + domain.lower(), "root" + domain.lower() ]: self.domain = d return self._get_by_key(key, self.domain) self.module.fail_json(msg="Domain '%s' not found" % domain) def get_tags(self, resource=None): existing_tags = [] for tag in resource.get('tags',[]): existing_tags.append({'key': tag['key'], 'value': tag['value']}) return existing_tags def _process_tags(self, resource, resource_type, tags, operation="create"): if tags: self.result['changed'] = True if not self.module.check_mode: args = {} args['resourceids'] = resource['id'] args['resourcetype'] = resource_type args['tags'] = tags if operation == "create": response = self.cs.createTags(**args) else: response = self.cs.deleteTags(**args) self.poll_job(response) def _tags_that_should_exist_or_be_updated(self, resource, tags): existing_tags = self.get_tags(resource) return [tag for tag in tags if tag not in existing_tags] def _tags_that_should_not_exist(self, resource, tags): existing_tags = self.get_tags(resource) return [tag for tag in existing_tags if tag not in tags] def ensure_tags(self, resource, resource_type=None): if not resource_type or not resource: self.module.fail_json(msg="Error: Missing resource or resource_type for tags.") if 'tags' in resource: tags = self.module.params.get('tags') if tags is not None: self._process_tags(resource, resource_type, self._tags_that_should_not_exist(resource, tags), operation="delete") self._process_tags(resource, resource_type, self._tags_that_should_exist_or_be_updated(resource, tags)) resource['tags'] = tags return resource def get_capabilities(self, key=None): if self.capabilities: return self._get_by_key(key, self.capabilities) capabilities = self.cs.listCapabilities() self.capabilities = capabilities['capability'] return self._get_by_key(key, self.capabilities) # TODO: for backward compatibility only, remove if not used anymore def _poll_job(self, job=None, key=None): return self.poll_job(job=job, key=key) def poll_job(self, job=None, key=None): if 'jobid' in job: while True: res = self.cs.queryAsyncJobResult(jobid=job['jobid']) if res['jobstatus'] != 0 and 'jobresult' in res: if 'errortext' in res['jobresult']: self.module.fail_json(msg="Failed: '%s'" % res['jobresult']['errortext']) if key and key in res['jobresult']: job = res['jobresult'][key] break time.sleep(2) return job def get_result(self, resource): if resource: returns = self.common_returns.copy() returns.update(self.returns) for search_key, return_key in returns.iteritems(): if search_key in resource: self.result[return_key] = resource[search_key] # Bad bad API does not always return int when it should. for search_key, return_key in self.returns_to_int.iteritems(): if search_key in resource: self.result[return_key] = int(resource[search_key]) # Special handling for tags if 'tags' in resource: self.result['tags'] = [] for tag in resource['tags']: result_tag = {} result_tag['key'] = tag['key'] result_tag['value'] = tag['value'] self.result['tags'].append(result_tag) return self.result
class AnsibleCloudStack(object): def __init__(self, module): if not has_lib_cs: module.fail_json(msg="python library cs required: pip install cs") self.result = {"changed": False} # Common returns, will be merged with self.returns # search_for_key: replace_with_key self.common_returns = { "id": "id", "name": "name", "created": "created", "zonename": "zone", "state": "state", "project": "project", "account": "account", "domain": "domain", "displaytext": "display_text", "displayname": "display_name", "description": "description", } # Init returns dict for use in subclasses self.returns = {} self.module = module self._connect() self.domain = None self.account = None self.project = None self.ip_address = None self.zone = None self.vm = None self.os_type = None self.hypervisor = None self.capabilities = None def _connect(self): api_key = self.module.params.get("api_key") api_secret = self.module.params.get("secret_key") api_url = self.module.params.get("api_url") api_http_method = self.module.params.get("api_http_method") api_timeout = self.module.params.get("api_timeout") if api_key and api_secret and api_url: self.cs = CloudStack( endpoint=api_url, key=api_key, secret=api_secret, timeout=api_timeout, method=api_http_method ) else: api_region = self.module.params.get("api_region", "cloudstack") self.cs = CloudStack(**read_config(api_region)) def get_or_fallback(self, key=None, fallback_key=None): value = self.module.params.get(key) if not value: value = self.module.params.get(fallback_key) return value # TODO: for backward compatibility only, remove if not used anymore def _has_changed(self, want_dict, current_dict, only_keys=None): return self.has_changed(want_dict=want_dict, current_dict=current_dict, only_keys=only_keys) def has_changed(self, want_dict, current_dict, only_keys=None): for key, value in want_dict.iteritems(): # Optionally limit by a list of keys if only_keys and key not in only_keys: continue # Skip None values if value is None: continue if key in current_dict: # API returns string for int in some cases, just to make sure if isinstance(value, int): current_dict[key] = int(current_dict[key]) elif isinstance(value, str): current_dict[key] = str(current_dict[key]) # Only need to detect a singe change, not every item if value != current_dict[key]: return True return False def _get_by_key(self, key=None, my_dict={}): if key: if key in my_dict: return my_dict[key] self.module.fail_json(msg="Something went wrong: %s not found" % key) return my_dict def get_project(self, key=None): if self.project: return self._get_by_key(key, self.project) project = self.module.params.get("project") if not project: return None args = {} args["account"] = self.get_account(key="name") args["domainid"] = self.get_domain(key="id") projects = self.cs.listProjects(**args) if projects: for p in projects["project"]: if project.lower() in [p["name"].lower(), p["id"]]: self.project = p return self._get_by_key(key, self.project) self.module.fail_json(msg="project '%s' not found" % project) def get_ip_address(self, key=None): if self.ip_address: return self._get_by_key(key, self.ip_address) ip_address = self.module.params.get("ip_address") if not ip_address: self.module.fail_json(msg="IP address param 'ip_address' is required") args = {} args["ipaddress"] = ip_address args["account"] = self.get_account(key="name") args["domainid"] = self.get_domain(key="id") args["projectid"] = self.get_project(key="id") ip_addresses = self.cs.listPublicIpAddresses(**args) if not ip_addresses: self.module.fail_json(msg="IP address '%s' not found" % args["ipaddress"]) self.ip_address = ip_addresses["publicipaddress"][0] return self._get_by_key(key, self.ip_address) def get_vm(self, key=None): if self.vm: return self._get_by_key(key, self.vm) vm = self.module.params.get("vm") if not vm: self.module.fail_json(msg="Virtual machine param 'vm' is required") args = {} args["account"] = self.get_account(key="name") args["domainid"] = self.get_domain(key="id") args["projectid"] = self.get_project(key="id") args["zoneid"] = self.get_zone(key="id") vms = self.cs.listVirtualMachines(**args) if vms: for v in vms["virtualmachine"]: if vm in [v["name"], v["displayname"], v["id"]]: self.vm = v return self._get_by_key(key, self.vm) self.module.fail_json(msg="Virtual machine '%s' not found" % vm) def get_zone(self, key=None): if self.zone: return self._get_by_key(key, self.zone) zone = self.module.params.get("zone") zones = self.cs.listZones() # use the first zone if no zone param given if not zone: self.zone = zones["zone"][0] return self._get_by_key(key, self.zone) if zones: for z in zones["zone"]: if zone in [z["name"], z["id"]]: self.zone = z return self._get_by_key(key, self.zone) self.module.fail_json(msg="zone '%s' not found" % zone) def get_os_type(self, key=None): if self.os_type: return self._get_by_key(key, self.zone) os_type = self.module.params.get("os_type") if not os_type: return None os_types = self.cs.listOsTypes() if os_types: for o in os_types["ostype"]: if os_type in [o["description"], o["id"]]: self.os_type = o return self._get_by_key(key, self.os_type) self.module.fail_json(msg="OS type '%s' not found" % os_type) def get_hypervisor(self): if self.hypervisor: return self.hypervisor hypervisor = self.module.params.get("hypervisor") hypervisors = self.cs.listHypervisors() # use the first hypervisor if no hypervisor param given if not hypervisor: self.hypervisor = hypervisors["hypervisor"][0]["name"] return self.hypervisor for h in hypervisors["hypervisor"]: if hypervisor.lower() == h["name"].lower(): self.hypervisor = h["name"] return self.hypervisor self.module.fail_json(msg="Hypervisor '%s' not found" % hypervisor) def get_account(self, key=None): if self.account: return self._get_by_key(key, self.account) account = self.module.params.get("account") if not account: return None domain = self.module.params.get("domain") if not domain: self.module.fail_json(msg="Account must be specified with Domain") args = {} args["name"] = account args["domainid"] = self.get_domain(key="id") args["listall"] = True accounts = self.cs.listAccounts(**args) if accounts: self.account = accounts["account"][0] return self._get_by_key(key, self.account) self.module.fail_json(msg="Account '%s' not found" % account) def get_domain(self, key=None): if self.domain: return self._get_by_key(key, self.domain) domain = self.module.params.get("domain") if not domain: return None args = {} args["listall"] = True domains = self.cs.listDomains(**args) if domains: for d in domains["domain"]: if d["path"].lower() in [domain.lower(), "root/" + domain.lower(), "root" + domain.lower()]: self.domain = d return self._get_by_key(key, self.domain) self.module.fail_json(msg="Domain '%s' not found" % domain) def get_tags(self, resource=None): existing_tags = self.cs.listTags(resourceid=resource["id"]) if existing_tags: return existing_tags["tag"] return [] def _delete_tags(self, resource, resource_type, tags): existing_tags = resource["tags"] tags_to_delete = [] for existing_tag in existing_tags: if existing_tag["key"] in tags: if existing_tag["value"] != tags[key]: tags_to_delete.append(existing_tag) else: tags_to_delete.append(existing_tag) if tags_to_delete: self.result["changed"] = True if not self.module.check_mode: args = {} args["resourceids"] = resource["id"] args["resourcetype"] = resource_type args["tags"] = tags_to_delete self.cs.deleteTags(**args) def _create_tags(self, resource, resource_type, tags): tags_to_create = [] for i, tag_entry in enumerate(tags): tag = {"key": tag_entry["key"], "value": tag_entry["value"]} tags_to_create.append(tag) if tags_to_create: self.result["changed"] = True if not self.module.check_mode: args = {} args["resourceids"] = resource["id"] args["resourcetype"] = resource_type args["tags"] = tags_to_create self.cs.createTags(**args) def ensure_tags(self, resource, resource_type=None): if not resource_type or not resource: self.module.fail_json(msg="Error: Missing resource or resource_type for tags.") if "tags" in resource: tags = self.module.params.get("tags") if tags is not None: self._delete_tags(resource, resource_type, tags) self._create_tags(resource, resource_type, tags) resource["tags"] = self.get_tags(resource) return resource def get_disk_offering(self, key=None): disk_offering = self.module.params.get("disk_offering") if not disk_offering: return None disk_offerings = self.cs.listDiskOfferings() if disk_offerings: for d in disk_offerings["diskoffering"]: if disk_offering in [d["displaytext"], d["name"], d["id"]]: return self._get_by_key(key, d) self.module.fail_json(msg="Disk offering '%s' not found" % disk_offering) def get_capabilities(self, key=None): if self.capabilities: return self._get_by_key(key, self.capabilities) capabilities = self.cs.listCapabilities() self.capabilities = capabilities["capability"] return self._get_by_key(key, self.capabilities) # TODO: for backward compatibility only, remove it if not used anymore def _poll_job(self, job=None, key=None): return self.poll_job(job=job, key=key) def poll_job(self, job=None, key=None): if "jobid" in job: while True: res = self.cs.queryAsyncJobResult(jobid=job["jobid"]) if res["jobstatus"] != 0 and "jobresult" in res: if "errortext" in res["jobresult"]: self.module.fail_json(msg="Failed: '%s'" % res["jobresult"]["errortext"]) if key and key in res["jobresult"]: job = res["jobresult"][key] break time.sleep(2) return job def get_result(self, resource): if resource: returns = self.common_returns.copy() returns.update(self.returns) for search_key, return_key in returns.iteritems(): if search_key in resource: self.result[return_key] = resource[search_key] # Special handling for tags if "tags" in resource: self.result["tags"] = [] for tag in resource["tags"]: result_tag = {} result_tag["key"] = tag["key"] result_tag["value"] = tag["value"] self.result["tags"].append(result_tag) return self.result
class AnsibleCloudStack: def __init__(self, module): if not has_lib_cs: module.fail_json(msg="python library cs required: pip install cs") self.result = { 'changed': False, } self.module = module self._connect() self.domain = None self.account = None self.project = None self.ip_address = None self.zone = None self.vm = None self.os_type = None self.hypervisor = None self.capabilities = None def _connect(self): api_key = self.module.params.get('api_key') api_secret = self.module.params.get('secret_key') api_url = self.module.params.get('api_url') api_http_method = self.module.params.get('api_http_method') if api_key and api_secret and api_url: self.cs = CloudStack( endpoint=api_url, key=api_key, secret=api_secret, method=api_http_method ) else: self.cs = CloudStack(**read_config()) # TODO: rename to has_changed() def _has_changed(self, want_dict, current_dict, only_keys=None): for key, value in want_dict.iteritems(): # Optionally limit by a list of keys if only_keys and key not in only_keys: continue; # Skip None values if value is None: continue; if key in current_dict: # API returns string for int in some cases, just to make sure if isinstance(value, int): current_dict[key] = int(current_dict[key]) elif isinstance(value, str): current_dict[key] = str(current_dict[key]) # Only need to detect a singe change, not every item if value != current_dict[key]: return True return False def _get_by_key(self, key=None, my_dict={}): if key: if key in my_dict: return my_dict[key] self.module.fail_json(msg="Something went wrong: %s not found" % key) return my_dict # TODO: for backward compatibility only, remove if not used anymore def get_project_id(self): return self.get_project(key='id') def get_project(self, key=None): if self.project: return self._get_by_key(key, self.project) project = self.module.params.get('project') if not project: return None args = {} args['listall'] = True args['account'] = self.get_account(key='name') args['domainid'] = self.get_domain(key='id') projects = self.cs.listProjects(**args) if projects: for p in projects['project']: if project in [ p['name'], p['displaytext'], p['id'] ]: self.project = p return self._get_by_key(key, self.project) self.module.fail_json(msg="project '%s' not found" % project) # TODO: for backward compatibility only, remove if not used anymore def get_ip_address_id(self): return self.get_ip_address(key='id') def get_ip_address(self, key=None): if self.ip_address: return self._get_by_key(key, self.ip_address) ip_address = self.module.params.get('ip_address') if not ip_address: self.module.fail_json(msg="IP address param 'ip_address' is required") args = {} args['ipaddress'] = ip_address args['account'] = self.get_account(key='name') args['domainid'] = self.get_domain(key='id') args['projectid'] = self.get_project(key='id') ip_addresses = self.cs.listPublicIpAddresses(**args) if not ip_addresses: self.module.fail_json(msg="IP address '%s' not found" % args['ipaddress']) self.ip_address = ip_addresses['publicipaddress'][0] return self._get_by_key(key, self.ip_address) # TODO: for backward compatibility only, remove if not used anymore def get_vm_id(self): return self.get_vm(key='id') def get_vm(self, key=None): if self.vm: return self._get_by_key(key, self.vm) vm = self.module.params.get('vm') if not vm: self.module.fail_json(msg="Virtual machine param 'vm' is required") args = {} args['account'] = self.get_account(key='name') args['domainid'] = self.get_domain(key='id') args['projectid'] = self.get_project(key='id') args['zoneid'] = self.get_zone(key='id') vms = self.cs.listVirtualMachines(**args) if vms: for v in vms['virtualmachine']: if vm in [ v['name'], v['displayname'], v['id'] ]: self.vm = v return self._get_by_key(key, self.vm) self.module.fail_json(msg="Virtual machine '%s' not found" % vm) # TODO: for backward compatibility only, remove if not used anymore def get_zone_id(self): return self.get_zone(key='id') def get_zone(self, key=None): if self.zone: return self._get_by_key(key, self.zone) zone = self.module.params.get('zone') zones = self.cs.listZones() # use the first zone if no zone param given if not zone: self.zone = zones['zone'][0] return self._get_by_key(key, self.zone) if zones: for z in zones['zone']: if zone in [ z['name'], z['id'] ]: self.zone = z return self._get_by_key(key, self.zone) self.module.fail_json(msg="zone '%s' not found" % zone) # TODO: for backward compatibility only, remove if not used anymore def get_os_type_id(self): return self.get_os_type(key='id') def get_os_type(self, key=None): if self.os_type: return self._get_by_key(key, self.zone) os_type = self.module.params.get('os_type') if not os_type: return None os_types = self.cs.listOsTypes() if os_types: for o in os_types['ostype']: if os_type in [ o['description'], o['id'] ]: self.os_type = o return self._get_by_key(key, self.os_type) self.module.fail_json(msg="OS type '%s' not found" % os_type) def get_hypervisor(self): if self.hypervisor: return self.hypervisor hypervisor = self.module.params.get('hypervisor') hypervisors = self.cs.listHypervisors() # use the first hypervisor if no hypervisor param given if not hypervisor: self.hypervisor = hypervisors['hypervisor'][0]['name'] return self.hypervisor for h in hypervisors['hypervisor']: if hypervisor.lower() == h['name'].lower(): self.hypervisor = h['name'] return self.hypervisor self.module.fail_json(msg="Hypervisor '%s' not found" % hypervisor) def get_account(self, key=None): if self.account: return self._get_by_key(key, self.account) account = self.module.params.get('account') if not account: return None domain = self.module.params.get('domain') if not domain: self.module.fail_json(msg="Account must be specified with Domain") args = {} args['name'] = account args['domainid'] = self.get_domain(key='id') args['listall'] = True accounts = self.cs.listAccounts(**args) if accounts: self.account = accounts['account'][0] return self._get_by_key(key, self.account) self.module.fail_json(msg="Account '%s' not found" % account) def get_domain(self, key=None): if self.domain: return self._get_by_key(key, self.domain) domain = self.module.params.get('domain') if not domain: return None args = {} args['name'] = domain args['listall'] = True domains = self.cs.listDomains(**args) if domains: self.domain = domains['domain'][0] return self._get_by_key(key, self.domain) self.module.fail_json(msg="Domain '%s' not found" % domain) def get_tags(self, resource=None): existing_tags = self.cs.listTags(resourceid=resource['id']) if existing_tags: return existing_tags['tag'] return [] def _delete_tags(self, resource, resource_type, tags): existing_tags = resource['tags'] tags_to_delete = [] for existing_tag in existing_tags: if existing_tag['key'] in tags: if existing_tag['value'] != tags[key]: tags_to_delete.append(existing_tag) else: tags_to_delete.append(existing_tag) if tags_to_delete: self.result['changed'] = True if not self.module.check_mode: args = {} args['resourceids'] = resource['id'] args['resourcetype'] = resource_type args['tags'] = tags_to_delete self.cs.deleteTags(**args) def _create_tags(self, resource, resource_type, tags): tags_to_create = [] for i, tag_entry in enumerate(tags): tag = { 'key': tag_entry['key'], 'value': tag_entry['value'], } tags_to_create.append(tag) if tags_to_create: self.result['changed'] = True if not self.module.check_mode: args = {} args['resourceids'] = resource['id'] args['resourcetype'] = resource_type args['tags'] = tags_to_create self.cs.createTags(**args) def ensure_tags(self, resource, resource_type=None): if not resource_type or not resource: self.module.fail_json(msg="Error: Missing resource or resource_type for tags.") if 'tags' in resource: tags = self.module.params.get('tags') if tags is not None: self._delete_tags(resource, resource_type, tags) self._create_tags(resource, resource_type, tags) resource['tags'] = self.get_tags(resource) return resource def get_capabilities(self, key=None): if self.capabilities: return self._get_by_key(key, self.capabilities) capabilities = self.cs.listCapabilities() self.capabilities = capabilities['capability'] return self._get_by_key(key, self.capabilities) # TODO: rename to poll_job() def _poll_job(self, job=None, key=None): if 'jobid' in job: while True: res = self.cs.queryAsyncJobResult(jobid=job['jobid']) if res['jobstatus'] != 0 and 'jobresult' in res: if 'errortext' in res['jobresult']: self.module.fail_json(msg="Failed: '%s'" % res['jobresult']['errortext']) if key and key in res['jobresult']: job = res['jobresult'][key] break time.sleep(2) return job
class AnsibleCloudStack(object): def __init__(self, module): if not has_lib_cs: module.fail_json(msg="python library cs required: pip install cs") self.result = { 'changed': False, 'diff': { 'before': dict(), 'after': dict() } } # Common returns, will be merged with self.returns # search_for_key: replace_with_key self.common_returns = { 'id': 'id', 'name': 'name', 'created': 'created', 'zonename': 'zone', 'state': 'state', 'project': 'project', 'account': 'account', 'domain': 'domain', 'displaytext': 'display_text', 'displayname': 'display_name', 'description': 'description', } # Init returns dict for use in subclasses self.returns = {} # these values will be casted to int self.returns_to_int = {} # these keys will be compared case sensitive in self.has_changed() self.case_sensitive_keys = [ 'id', 'displaytext', 'displayname', 'description', ] self.module = module self._connect() # Helper for VPCs self._vpc_networks_ids = None self.domain = None self.account = None self.project = None self.ip_address = None self.network = None self.vpc = None self.zone = None self.vm = None self.vm_default_nic = None self.os_type = None self.hypervisor = None self.capabilities = None def _connect(self): api_key = self.module.params.get('api_key') api_secret = self.module.params.get('api_secret') api_url = self.module.params.get('api_url') api_http_method = self.module.params.get('api_http_method') api_timeout = self.module.params.get('api_timeout') if api_key and api_secret and api_url: self.cs = CloudStack(endpoint=api_url, key=api_key, secret=api_secret, timeout=api_timeout, method=api_http_method) else: api_region = self.module.params.get('api_region', 'cloudstack') self.cs = CloudStack(**read_config(api_region)) def get_or_fallback(self, key=None, fallback_key=None): value = self.module.params.get(key) if not value: value = self.module.params.get(fallback_key) return value # TODO: for backward compatibility only, remove if not used anymore def _has_changed(self, want_dict, current_dict, only_keys=None): return self.has_changed(want_dict=want_dict, current_dict=current_dict, only_keys=only_keys) def has_changed(self, want_dict, current_dict, only_keys=None): result = False for key, value in want_dict.items(): # Optionally limit by a list of keys if only_keys and key not in only_keys: continue # Skip None values if value is None: continue if key in current_dict: if isinstance(value, (int, float, long, complex)): # ensure we compare the same type if isinstance(value, int): current_dict[key] = int(current_dict[key]) elif isinstance(value, float): current_dict[key] = float(current_dict[key]) elif isinstance(value, long): current_dict[key] = long(current_dict[key]) elif isinstance(value, complex): current_dict[key] = complex(current_dict[key]) if value != current_dict[key]: self.result['diff']['before'][key] = current_dict[key] self.result['diff']['after'][key] = value result = True else: if self.case_sensitive_keys and key in self.case_sensitive_keys: if value != current_dict[key].encode('utf-8'): self.result['diff']['before'][key] = current_dict[ key].encode('utf-8') self.result['diff']['after'][key] = value result = True # Test for diff in case insensitive way elif value.lower() != current_dict[key].encode( 'utf-8').lower(): self.result['diff']['before'][key] = current_dict[ key].encode('utf-8') self.result['diff']['after'][key] = value result = True else: self.result['diff']['before'][key] = None self.result['diff']['after'][key] = value result = True return result def _get_by_key(self, key=None, my_dict=None): if my_dict is None: my_dict = {} if key: if key in my_dict: return my_dict[key] self.module.fail_json(msg="Something went wrong: %s not found" % key) return my_dict def get_vpc(self, key=None): """Return a VPC dictionary or the value of given key of.""" if self.vpc: return self._get_by_key(key, self.vpc) vpc = self.module.params.get('vpc') if not vpc: vpc = os.environ.get('CLOUDSTACK_VPC') if not vpc: return None args = { 'account': self.get_account(key='name'), 'domainid': self.get_domain(key='id'), 'projectid': self.get_project(key='id'), 'zoneid': self.get_zone(key='id'), } vpcs = self.cs.listVPCs(**args) if not vpcs: self.module.fail_json(msg="No VPCs available.") for v in vpcs['vpc']: if vpc in [v['displaytext'], v['name'], v['id']]: self.vpc = v return self._get_by_key(key, self.vpc) self.module.fail_json(msg="VPC '%s' not found" % vpc) def is_vm_in_vpc(self, vm): for n in vm.get('nic'): if n.get('isdefault', False): return self.is_vpc_network(network_id=n['networkid']) self.module.fail_json(msg="VM has no default nic") def is_vpc_network(self, network_id): """Returns True if network is in VPC.""" # This is an efficient way to query a lot of networks at a time if self._vpc_networks_ids is None: args = { 'account': self.get_account(key='name'), 'domainid': self.get_domain(key='id'), 'projectid': self.get_project(key='id'), 'zoneid': self.get_zone(key='id'), } vpcs = self.cs.listVPCs(**args) self._vpc_networks_ids = [] if vpcs: for vpc in vpcs['vpc']: for n in vpc.get('network', []): self._vpc_networks_ids.append(n['id']) return network_id in self._vpc_networks_ids def get_network(self, key=None): """Return a network dictionary or the value of given key of.""" if self.network: return self._get_by_key(key, self.network) network = self.module.params.get('network') if not network: return None args = { 'account': self.get_account(key='name'), 'domainid': self.get_domain(key='id'), 'projectid': self.get_project(key='id'), 'zoneid': self.get_zone(key='id'), 'vpcid': self.get_vpc(key='id') } networks = self.cs.listNetworks(**args) if not networks: self.module.fail_json(msg="No networks available.") for n in networks['network']: # ignore any VPC network if vpc param is not given if 'vpcid' in n and not self.get_vpc(key='id'): continue if network in [n['displaytext'], n['name'], n['id']]: self.network = n return self._get_by_key(key, self.network) self.module.fail_json(msg="Network '%s' not found" % network) def get_project(self, key=None): if self.project: return self._get_by_key(key, self.project) project = self.module.params.get('project') if not project: project = os.environ.get('CLOUDSTACK_PROJECT') if not project: return None args = {} args['account'] = self.get_account(key='name') args['domainid'] = self.get_domain(key='id') projects = self.cs.listProjects(**args) if projects: for p in projects['project']: if project.lower() in [p['name'].lower(), p['id']]: self.project = p return self._get_by_key(key, self.project) self.module.fail_json(msg="project '%s' not found" % project) def get_ip_address(self, key=None): if self.ip_address: return self._get_by_key(key, self.ip_address) ip_address = self.module.params.get('ip_address') if not ip_address: self.module.fail_json( msg="IP address param 'ip_address' is required") args = { 'ipaddress': ip_address, 'account': self.get_account(key='name'), 'domainid': self.get_domain(key='id'), 'projectid': self.get_project(key='id'), 'vpcid': self.get_vpc(key='id'), } ip_addresses = self.cs.listPublicIpAddresses(**args) if not ip_addresses: self.module.fail_json(msg="IP address '%s' not found" % args['ipaddress']) self.ip_address = ip_addresses['publicipaddress'][0] return self._get_by_key(key, self.ip_address) def get_vm_guest_ip(self): vm_guest_ip = self.module.params.get('vm_guest_ip') default_nic = self.get_vm_default_nic() if not vm_guest_ip: return default_nic['ipaddress'] for secondary_ip in default_nic['secondaryip']: if vm_guest_ip == secondary_ip['ipaddress']: return vm_guest_ip self.module.fail_json(msg="Secondary IP '%s' not assigned to VM" % vm_guest_ip) def get_vm_default_nic(self): if self.vm_default_nic: return self.vm_default_nic nics = self.cs.listNics(virtualmachineid=self.get_vm(key='id')) if nics: for n in nics['nic']: if n['isdefault']: self.vm_default_nic = n return self.vm_default_nic self.module.fail_json(msg="No default IP address of VM '%s' found" % self.module.params.get('vm')) def get_vm(self, key=None): if self.vm: return self._get_by_key(key, self.vm) vm = self.module.params.get('vm') if not vm: self.module.fail_json(msg="Virtual machine param 'vm' is required") vpc_id = self.get_vpc(key='id') args = { 'account': self.get_account(key='name'), 'domainid': self.get_domain(key='id'), 'projectid': self.get_project(key='id'), 'zoneid': self.get_zone(key='id'), 'vpcid': vpc_id, } vms = self.cs.listVirtualMachines(**args) if vms: for v in vms['virtualmachine']: # Due the limitation of the API, there is no easy way (yet) to get only those VMs # not belonging to a VPC. if not vpc_id and self.is_vm_in_vpc(vm=v): continue if vm.lower() in [ v['name'].lower(), v['displayname'].lower(), v['id'] ]: self.vm = v return self._get_by_key(key, self.vm) self.module.fail_json(msg="Virtual machine '%s' not found" % vm) def get_zone(self, key=None): if self.zone: return self._get_by_key(key, self.zone) zone = self.module.params.get('zone') if not zone: zone = os.environ.get('CLOUDSTACK_ZONE') zones = self.cs.listZones() if not zones: self.module.fail_json( msg="No zones available. Please create a zone first") # use the first zone if no zone param given if not zone: self.zone = zones['zone'][0] return self._get_by_key(key, self.zone) if zones: for z in zones['zone']: if zone.lower() in [z['name'].lower(), z['id']]: self.zone = z return self._get_by_key(key, self.zone) self.module.fail_json(msg="zone '%s' not found" % zone) def get_os_type(self, key=None): if self.os_type: return self._get_by_key(key, self.zone) os_type = self.module.params.get('os_type') if not os_type: return None os_types = self.cs.listOsTypes() if os_types: for o in os_types['ostype']: if os_type in [o['description'], o['id']]: self.os_type = o return self._get_by_key(key, self.os_type) self.module.fail_json(msg="OS type '%s' not found" % os_type) def get_hypervisor(self): if self.hypervisor: return self.hypervisor hypervisor = self.module.params.get('hypervisor') hypervisors = self.cs.listHypervisors() # use the first hypervisor if no hypervisor param given if not hypervisor: self.hypervisor = hypervisors['hypervisor'][0]['name'] return self.hypervisor for h in hypervisors['hypervisor']: if hypervisor.lower() == h['name'].lower(): self.hypervisor = h['name'] return self.hypervisor self.module.fail_json(msg="Hypervisor '%s' not found" % hypervisor) def get_account(self, key=None): if self.account: return self._get_by_key(key, self.account) account = self.module.params.get('account') if not account: account = os.environ.get('CLOUDSTACK_ACCOUNT') if not account: return None domain = self.module.params.get('domain') if not domain: self.module.fail_json(msg="Account must be specified with Domain") args = {} args['name'] = account args['domainid'] = self.get_domain(key='id') args['listall'] = True accounts = self.cs.listAccounts(**args) if accounts: self.account = accounts['account'][0] return self._get_by_key(key, self.account) self.module.fail_json(msg="Account '%s' not found" % account) def get_domain(self, key=None): if self.domain: return self._get_by_key(key, self.domain) domain = self.module.params.get('domain') if not domain: domain = os.environ.get('CLOUDSTACK_DOMAIN') if not domain: return None args = {} args['listall'] = True domains = self.cs.listDomains(**args) if domains: for d in domains['domain']: if d['path'].lower() in [ domain.lower(), "root/" + domain.lower(), "root" + domain.lower() ]: self.domain = d return self._get_by_key(key, self.domain) self.module.fail_json(msg="Domain '%s' not found" % domain) def get_tags(self, resource=None): existing_tags = [] for tag in resource.get('tags', []): existing_tags.append({'key': tag['key'], 'value': tag['value']}) return existing_tags def _process_tags(self, resource, resource_type, tags, operation="create"): if tags: self.result['changed'] = True if not self.module.check_mode: args = {} args['resourceids'] = resource['id'] args['resourcetype'] = resource_type args['tags'] = tags if operation == "create": response = self.cs.createTags(**args) else: response = self.cs.deleteTags(**args) self.poll_job(response) def _tags_that_should_exist_or_be_updated(self, resource, tags): existing_tags = self.get_tags(resource) return [tag for tag in tags if tag not in existing_tags] def _tags_that_should_not_exist(self, resource, tags): existing_tags = self.get_tags(resource) return [tag for tag in existing_tags if tag not in tags] def ensure_tags(self, resource, resource_type=None): if not resource_type or not resource: self.module.fail_json( msg="Error: Missing resource or resource_type for tags.") if 'tags' in resource: tags = self.module.params.get('tags') if tags is not None: self._process_tags(resource, resource_type, self._tags_that_should_not_exist( resource, tags), operation="delete") self._process_tags( resource, resource_type, self._tags_that_should_exist_or_be_updated(resource, tags)) resource['tags'] = tags return resource def get_capabilities(self, key=None): if self.capabilities: return self._get_by_key(key, self.capabilities) capabilities = self.cs.listCapabilities() self.capabilities = capabilities['capability'] return self._get_by_key(key, self.capabilities) # TODO: for backward compatibility only, remove if not used anymore def _poll_job(self, job=None, key=None): return self.poll_job(job=job, key=key) def poll_job(self, job=None, key=None): if 'jobid' in job: while True: res = self.cs.queryAsyncJobResult(jobid=job['jobid']) if res['jobstatus'] != 0 and 'jobresult' in res: if 'errortext' in res['jobresult']: self.module.fail_json(msg="Failed: '%s'" % res['jobresult']['errortext']) if key and key in res['jobresult']: job = res['jobresult'][key] break time.sleep(2) return job def get_result(self, resource): if resource: returns = self.common_returns.copy() returns.update(self.returns) for search_key, return_key in returns.items(): if search_key in resource: self.result[return_key] = resource[search_key] # Bad bad API does not always return int when it should. for search_key, return_key in self.returns_to_int.items(): if search_key in resource: self.result[return_key] = int(resource[search_key]) # Special handling for tags if 'tags' in resource: self.result['tags'] = [] for tag in resource['tags']: result_tag = {} result_tag['key'] = tag['key'] result_tag['value'] = tag['value'] self.result['tags'].append(result_tag) return self.result
class AnsibleCloudStack(object): def __init__(self, module): if not has_lib_cs: module.fail_json(msg="python library cs required: pip install cs") self.result = { 'changed': False, } # Common returns, will be merged with self.returns # search_for_key: replace_with_key self.common_returns = { 'id': 'id', 'name': 'name', 'created': 'created', 'zonename': 'zone', 'state': 'state', 'project': 'project', 'account': 'account', 'domain': 'domain', 'displaytext': 'display_text', 'displayname': 'display_name', 'description': 'description', } # Init returns dict for use in subclasses self.returns = {} # these values will be casted to int self.returns_to_int = {} self.module = module self._connect() self.domain = None self.account = None self.project = None self.ip_address = None self.zone = None self.vm = None self.os_type = None self.hypervisor = None self.capabilities = None self.tags = None def _connect(self): api_key = self.module.params.get('api_key') api_secret = self.module.params.get('secret_key') api_url = self.module.params.get('api_url') api_http_method = self.module.params.get('api_http_method') api_timeout = self.module.params.get('api_timeout') if api_key and api_secret and api_url: self.cs = CloudStack( endpoint=api_url, key=api_key, secret=api_secret, timeout=api_timeout, method=api_http_method) else: api_region = self.module.params.get('api_region', 'cloudstack') self.cs = CloudStack(**read_config(api_region)) def get_or_fallback(self, key=None, fallback_key=None): value = self.module.params.get(key) if not value: value = self.module.params.get(fallback_key) return value # TODO: for backward compatibility only, remove if not used anymore def _has_changed(self, want_dict, current_dict, only_keys=None): return self.has_changed( want_dict=want_dict, current_dict=current_dict, only_keys=only_keys) def has_changed(self, want_dict, current_dict, only_keys=None): for key, value in want_dict.iteritems(): # Optionally limit by a list of keys if only_keys and key not in only_keys: continue # Skip None values if value is None: continue if key in current_dict: # API returns string for int in some cases, just to make sure if isinstance(value, int): current_dict[key] = int(current_dict[key]) elif isinstance(value, str): current_dict[key] = str(current_dict[key]) # Only need to detect a singe change, not every item if value != current_dict[key]: return True return False def _get_by_key(self, key=None, my_dict=None): if my_dict is None: my_dict = {} if key: if key in my_dict: return my_dict[key] self.module.fail_json( msg="Something went wrong: %s not found" % key) return my_dict def get_project(self, key=None): if self.project: return self._get_by_key(key, self.project) project = self.module.params.get('project') if not project: return None args = {} args['account'] = self.get_account(key='name') args['domainid'] = self.get_domain(key='id') projects = self.cs.listProjects(**args) if projects: for p in projects['project']: if project.lower() in [p['name'].lower(), p['id']]: self.project = p return self._get_by_key(key, self.project) self.module.fail_json(msg="project '%s' not found" % project) def get_ip_address(self, key=None): if self.ip_address: return self._get_by_key(key, self.ip_address) ip_address = self.module.params.get('ip_address') if not ip_address: self.module.fail_json( msg="IP address param 'ip_address' is required") args = {} args['ipaddress'] = ip_address args['account'] = self.get_account(key='name') args['domainid'] = self.get_domain(key='id') args['projectid'] = self.get_project(key='id') ip_addresses = self.cs.listPublicIpAddresses(**args) if not ip_addresses: self.module.fail_json( msg="IP address '%s' not found" % args['ipaddress']) self.ip_address = ip_addresses['publicipaddress'][0] return self._get_by_key(key, self.ip_address) def get_vm(self, key=None): if self.vm: return self._get_by_key(key, self.vm) vm = self.module.params.get('vm') if not vm: self.module.fail_json(msg="Virtual machine param 'vm' is required") args = {} args['account'] = self.get_account(key='name') args['domainid'] = self.get_domain(key='id') args['projectid'] = self.get_project(key='id') args['zoneid'] = self.get_zone(key='id') vms = self.cs.listVirtualMachines(**args) if vms: for v in vms['virtualmachine']: if vm in [v['name'], v['displayname'], v['id']]: self.vm = v return self._get_by_key(key, self.vm) self.module.fail_json(msg="Virtual machine '%s' not found" % vm) def get_zone(self, key=None): if self.zone: return self._get_by_key(key, self.zone) zone = self.module.params.get('zone') zones = self.cs.listZones() # use the first zone if no zone param given if not zone: self.zone = zones['zone'][0] return self._get_by_key(key, self.zone) if zones: for z in zones['zone']: if zone in [z['name'], z['id']]: self.zone = z return self._get_by_key(key, self.zone) self.module.fail_json(msg="zone '%s' not found" % zone) def get_os_type(self, key=None): if self.os_type: return self._get_by_key(key, self.zone) os_type = self.module.params.get('os_type') if not os_type: return None os_types = self.cs.listOsTypes() if os_types: for o in os_types['ostype']: if os_type in [o['description'], o['id']]: self.os_type = o return self._get_by_key(key, self.os_type) self.module.fail_json(msg="OS type '%s' not found" % os_type) def get_hypervisor(self): if self.hypervisor: return self.hypervisor hypervisor = self.module.params.get('hypervisor') hypervisors = self.cs.listHypervisors() # use the first hypervisor if no hypervisor param given if not hypervisor: self.hypervisor = hypervisors['hypervisor'][0]['name'] return self.hypervisor for h in hypervisors['hypervisor']: if hypervisor.lower() == h['name'].lower(): self.hypervisor = h['name'] return self.hypervisor self.module.fail_json(msg="Hypervisor '%s' not found" % hypervisor) def get_account(self, key=None): if self.account: return self._get_by_key(key, self.account) account = self.module.params.get('account') if not account: return None domain = self.module.params.get('domain') if not domain: self.module.fail_json(msg="Account must be specified with Domain") args = {} args['name'] = account args['domainid'] = self.get_domain(key='id') args['listall'] = True accounts = self.cs.listAccounts(**args) if accounts: self.account = accounts['account'][0] return self._get_by_key(key, self.account) self.module.fail_json(msg="Account '%s' not found" % account) def get_domain(self, key=None): if self.domain: return self._get_by_key(key, self.domain) domain = self.module.params.get('domain') if not domain: return None args = {} args['listall'] = True domains = self.cs.listDomains(**args) if domains: for d in domains['domain']: if d['path'].lower() in [ domain.lower(), "root/" + domain.lower(), "root" + domain.lower() ]: self.domain = d return self._get_by_key(key, self.domain) self.module.fail_json(msg="Domain '%s' not found" % domain) def get_tags(self, resource=None): if not self.tags: args = {} args['projectid'] = self.get_project(key='id') args['account'] = self.get_account(key='name') args['domainid'] = self.get_domain(key='id') args['resourceid'] = resource['id'] response = self.cs.listTags(**args) self.tags = response.get('tag', []) existing_tags = [] if self.tags: for tag in self.tags: existing_tags.append({ 'key': tag['key'], 'value': tag['value'] }) return existing_tags def _process_tags(self, resource, resource_type, tags, operation="create"): if tags: self.result['changed'] = True if not self.module.check_mode: args = {} args['resourceids'] = resource['id'] args['resourcetype'] = resource_type args['tags'] = tags if operation == "create": response = self.cs.createTags(**args) else: response = self.cs.deleteTags(**args) self.poll_job(response) def _tags_that_should_exist_or_be_updated(self, resource, tags): existing_tags = self.get_tags(resource) return [tag for tag in tags if tag not in existing_tags] def _tags_that_should_not_exist(self, resource, tags): existing_tags = self.get_tags(resource) return [tag for tag in existing_tags if tag not in tags] def ensure_tags(self, resource, resource_type=None): if not resource_type or not resource: self.module.fail_json( msg="Error: Missing resource or resource_type for tags.") if 'tags' in resource: tags = self.module.params.get('tags') if tags is not None: self._process_tags( resource, resource_type, self._tags_that_should_not_exist(resource, tags), operation="delete") self._process_tags( resource, resource_type, self._tags_that_should_exist_or_be_updated(resource, tags)) self.tags = None resource['tags'] = self.get_tags(resource) return resource def get_capabilities(self, key=None): if self.capabilities: return self._get_by_key(key, self.capabilities) capabilities = self.cs.listCapabilities() self.capabilities = capabilities['capability'] return self._get_by_key(key, self.capabilities) # TODO: for backward compatibility only, remove if not used anymore def _poll_job(self, job=None, key=None): return self.poll_job(job=job, key=key) def poll_job(self, job=None, key=None): if 'jobid' in job: while True: res = self.cs.queryAsyncJobResult(jobid=job['jobid']) if res['jobstatus'] != 0 and 'jobresult' in res: if 'errortext' in res['jobresult']: self.module.fail_json( msg="Failed: '%s'" % res['jobresult']['errortext']) if key and key in res['jobresult']: job = res['jobresult'][key] break time.sleep(2) return job def get_result(self, resource): if resource: returns = self.common_returns.copy() returns.update(self.returns) for search_key, return_key in returns.iteritems(): if search_key in resource: self.result[return_key] = resource[search_key] # Bad bad API does not always return int when it should. for search_key, return_key in self.returns_to_int.iteritems(): if search_key in resource: self.result[return_key] = int(resource[search_key]) # Special handling for tags if 'tags' in resource: self.result['tags'] = [] for tag in resource['tags']: result_tag = {} result_tag['key'] = tag['key'] result_tag['value'] = tag['value'] self.result['tags'].append(result_tag) return self.result
class AnsibleCloudStack(object): def __init__(self, module): if not has_lib_cs: module.fail_json(msg="python library cs required: pip install cs") self.result = { 'changed': False, } # Common returns, will be merged with self.returns # search_for_key: replace_with_key self.common_returns = { 'id': 'id', 'name': 'name', 'created': 'created', 'zonename': 'zone', 'state': 'state', 'project': 'project', 'account': 'account', 'domain': 'domain', 'displaytext': 'display_text', 'displayname': 'display_name', 'description': 'description', } # Init returns dict for use in subclasses self.returns = {} # these values will be casted to int self.returns_to_int = {} self.module = module self._connect() self.domain = None self.account = None self.project = None self.ip_address = None self.zone = None self.vm = None self.os_type = None self.hypervisor = None self.capabilities = None self.tags = None def _connect(self): api_key = self.module.params.get('api_key') api_secret = self.module.params.get('secret_key') api_url = self.module.params.get('api_url') api_http_method = self.module.params.get('api_http_method') api_timeout = self.module.params.get('api_timeout') if api_key and api_secret and api_url: self.cs = CloudStack(endpoint=api_url, key=api_key, secret=api_secret, timeout=api_timeout, method=api_http_method) else: api_region = self.module.params.get('api_region', 'cloudstack') self.cs = CloudStack(**read_config(api_region)) def get_or_fallback(self, key=None, fallback_key=None): value = self.module.params.get(key) if not value: value = self.module.params.get(fallback_key) return value # TODO: for backward compatibility only, remove if not used anymore def _has_changed(self, want_dict, current_dict, only_keys=None): return self.has_changed(want_dict=want_dict, current_dict=current_dict, only_keys=only_keys) def has_changed(self, want_dict, current_dict, only_keys=None): for key, value in want_dict.iteritems(): # Optionally limit by a list of keys if only_keys and key not in only_keys: continue # Skip None values if value is None: continue if key in current_dict: # API returns string for int in some cases, just to make sure if isinstance(value, int): current_dict[key] = int(current_dict[key]) elif isinstance(value, str): current_dict[key] = str(current_dict[key]) # Only need to detect a singe change, not every item if value != current_dict[key]: return True return False def _get_by_key(self, key=None, my_dict=None): if my_dict is None: my_dict = {} if key: if key in my_dict: return my_dict[key] self.module.fail_json(msg="Something went wrong: %s not found" % key) return my_dict def get_project(self, key=None): if self.project: return self._get_by_key(key, self.project) project = self.module.params.get('project') if not project: return None args = {} args['account'] = self.get_account(key='name') args['domainid'] = self.get_domain(key='id') projects = self.cs.listProjects(**args) if projects: for p in projects['project']: if project.lower() in [p['name'].lower(), p['id']]: self.project = p return self._get_by_key(key, self.project) self.module.fail_json(msg="project '%s' not found" % project) def get_ip_address(self, key=None): if self.ip_address: return self._get_by_key(key, self.ip_address) ip_address = self.module.params.get('ip_address') if not ip_address: self.module.fail_json( msg="IP address param 'ip_address' is required") args = {} args['ipaddress'] = ip_address args['account'] = self.get_account(key='name') args['domainid'] = self.get_domain(key='id') args['projectid'] = self.get_project(key='id') ip_addresses = self.cs.listPublicIpAddresses(**args) if not ip_addresses: self.module.fail_json(msg="IP address '%s' not found" % args['ipaddress']) self.ip_address = ip_addresses['publicipaddress'][0] return self._get_by_key(key, self.ip_address) def get_vm(self, key=None): if self.vm: return self._get_by_key(key, self.vm) vm = self.module.params.get('vm') if not vm: self.module.fail_json(msg="Virtual machine param 'vm' is required") args = {} args['account'] = self.get_account(key='name') args['domainid'] = self.get_domain(key='id') args['projectid'] = self.get_project(key='id') args['zoneid'] = self.get_zone(key='id') vms = self.cs.listVirtualMachines(**args) if vms: for v in vms['virtualmachine']: if vm in [v['name'], v['displayname'], v['id']]: self.vm = v return self._get_by_key(key, self.vm) self.module.fail_json(msg="Virtual machine '%s' not found" % vm) def get_zone(self, key=None): if self.zone: return self._get_by_key(key, self.zone) zone = self.module.params.get('zone') zones = self.cs.listZones() # use the first zone if no zone param given if not zone: self.zone = zones['zone'][0] return self._get_by_key(key, self.zone) if zones: for z in zones['zone']: if zone in [z['name'], z['id']]: self.zone = z return self._get_by_key(key, self.zone) self.module.fail_json(msg="zone '%s' not found" % zone) def get_os_type(self, key=None): if self.os_type: return self._get_by_key(key, self.zone) os_type = self.module.params.get('os_type') if not os_type: return None os_types = self.cs.listOsTypes() if os_types: for o in os_types['ostype']: if os_type in [o['description'], o['id']]: self.os_type = o return self._get_by_key(key, self.os_type) self.module.fail_json(msg="OS type '%s' not found" % os_type) def get_hypervisor(self): if self.hypervisor: return self.hypervisor hypervisor = self.module.params.get('hypervisor') hypervisors = self.cs.listHypervisors() # use the first hypervisor if no hypervisor param given if not hypervisor: self.hypervisor = hypervisors['hypervisor'][0]['name'] return self.hypervisor for h in hypervisors['hypervisor']: if hypervisor.lower() == h['name'].lower(): self.hypervisor = h['name'] return self.hypervisor self.module.fail_json(msg="Hypervisor '%s' not found" % hypervisor) def get_account(self, key=None): if self.account: return self._get_by_key(key, self.account) account = self.module.params.get('account') if not account: return None domain = self.module.params.get('domain') if not domain: self.module.fail_json(msg="Account must be specified with Domain") args = {} args['name'] = account args['domainid'] = self.get_domain(key='id') args['listall'] = True accounts = self.cs.listAccounts(**args) if accounts: self.account = accounts['account'][0] return self._get_by_key(key, self.account) self.module.fail_json(msg="Account '%s' not found" % account) def get_domain(self, key=None): if self.domain: return self._get_by_key(key, self.domain) domain = self.module.params.get('domain') if not domain: return None args = {} args['listall'] = True domains = self.cs.listDomains(**args) if domains: for d in domains['domain']: if d['path'].lower() in [ domain.lower(), "root/" + domain.lower(), "root" + domain.lower() ]: self.domain = d return self._get_by_key(key, self.domain) self.module.fail_json(msg="Domain '%s' not found" % domain) def get_tags(self, resource=None): if not self.tags: args = {} args['projectid'] = self.get_project(key='id') args['account'] = self.get_account(key='name') args['domainid'] = self.get_domain(key='id') args['resourceid'] = resource['id'] response = self.cs.listTags(**args) self.tags = response.get('tag', []) existing_tags = [] if self.tags: for tag in self.tags: existing_tags.append({ 'key': tag['key'], 'value': tag['value'] }) return existing_tags def _process_tags(self, resource, resource_type, tags, operation="create"): if tags: self.result['changed'] = True if not self.module.check_mode: args = {} args['resourceids'] = resource['id'] args['resourcetype'] = resource_type args['tags'] = tags if operation == "create": response = self.cs.createTags(**args) else: response = self.cs.deleteTags(**args) self.poll_job(response) def _tags_that_should_exist_or_be_updated(self, resource, tags): existing_tags = self.get_tags(resource) return [tag for tag in tags if tag not in existing_tags] def _tags_that_should_not_exist(self, resource, tags): existing_tags = self.get_tags(resource) return [tag for tag in existing_tags if tag not in tags] def ensure_tags(self, resource, resource_type=None): if not resource_type or not resource: self.module.fail_json( msg="Error: Missing resource or resource_type for tags.") if 'tags' in resource: tags = self.module.params.get('tags') if tags is not None: self._process_tags(resource, resource_type, self._tags_that_should_not_exist( resource, tags), operation="delete") self._process_tags( resource, resource_type, self._tags_that_should_exist_or_be_updated(resource, tags)) self.tags = None resource['tags'] = self.get_tags(resource) return resource def get_capabilities(self, key=None): if self.capabilities: return self._get_by_key(key, self.capabilities) capabilities = self.cs.listCapabilities() self.capabilities = capabilities['capability'] return self._get_by_key(key, self.capabilities) # TODO: for backward compatibility only, remove if not used anymore def _poll_job(self, job=None, key=None): return self.poll_job(job=job, key=key) def poll_job(self, job=None, key=None): if 'jobid' in job: while True: res = self.cs.queryAsyncJobResult(jobid=job['jobid']) if res['jobstatus'] != 0 and 'jobresult' in res: if 'errortext' in res['jobresult']: self.module.fail_json(msg="Failed: '%s'" % res['jobresult']['errortext']) if key and key in res['jobresult']: job = res['jobresult'][key] break time.sleep(2) return job def get_result(self, resource): if resource: returns = self.common_returns.copy() returns.update(self.returns) for search_key, return_key in returns.iteritems(): if search_key in resource: self.result[return_key] = resource[search_key] # Bad bad API does not always return int when it should. for search_key, return_key in self.returns_to_int.iteritems(): if search_key in resource: self.result[return_key] = int(resource[search_key]) # Special handling for tags if 'tags' in resource: self.result['tags'] = [] for tag in resource['tags']: result_tag = {} result_tag['key'] = tag['key'] result_tag['value'] = tag['value'] self.result['tags'].append(result_tag) return self.result
class AnsibleCloudStack: def __init__(self, module): if not has_lib_cs: module.fail_json(msg="python library cs required: pip install cs") self.module = module self._connect() self.project_id = None self.ip_address_id = None self.zone_id = None self.vm_id = None self.os_type_id = None self.hypervisor = None def _connect(self): api_key = self.module.params.get('api_key') api_secret = self.module.params.get('secret_key') api_url = self.module.params.get('api_url') api_http_method = self.module.params.get('api_http_method') if api_key and api_secret and api_url: self.cs = CloudStack(endpoint=api_url, key=api_key, secret=api_secret, method=api_http_method) else: self.cs = CloudStack(**read_config()) def get_project_id(self): if self.project_id: return self.project_id project = self.module.params.get('project') if not project: return None projects = self.cs.listProjects() if projects: for p in projects['project']: if project in [p['name'], p['displaytext'], p['id']]: self.project_id = p['id'] return self.project_id self.module.fail_json(msg="project '%s' not found" % project) def get_ip_address_id(self): if self.ip_address_id: return self.ip_address_id ip_address = self.module.params.get('ip_address') if not ip_address: self.module.fail_json( msg="IP address param 'ip_address' is required") args = {} args['ipaddress'] = ip_address args['projectid'] = self.get_project_id() ip_addresses = self.cs.listPublicIpAddresses(**args) if not ip_addresses: self.module.fail_json(msg="IP address '%s' not found" % args['ipaddress']) self.ip_address_id = ip_addresses['publicipaddress'][0]['id'] return self.ip_address_id def get_vm_id(self): if self.vm_id: return self.vm_id vm = self.module.params.get('vm') if not vm: self.module.fail_json(msg="Virtual machine param 'vm' is required") args = {} args['projectid'] = self.get_project_id() vms = self.cs.listVirtualMachines(**args) if vms: for v in vms['virtualmachine']: if vm in [v['name'], v['id']]: self.vm_id = v['id'] return self.vm_id self.module.fail_json(msg="Virtual machine '%s' not found" % vm) def get_zone_id(self): if self.zone_id: return self.zone_id zone = self.module.params.get('zone') zones = self.cs.listZones() # use the first zone if no zone param given if not zone: self.zone_id = zones['zone'][0]['id'] return self.zone_id if zones: for z in zones['zone']: if zone in [z['name'], z['id']]: self.zone_id = z['id'] return self.zone_id self.module.fail_json(msg="zone '%s' not found" % zone) def get_os_type_id(self): if self.os_type_id: return self.os_type_id os_type = self.module.params.get('os_type') if not os_type: return None os_types = self.cs.listOsTypes() if os_types: for o in os_types['ostype']: if os_type in [o['description'], o['id']]: self.os_type_id = o['id'] return self.os_type_id self.module.fail_json(msg="OS type '%s' not found" % os_type) def get_hypervisor(self): if self.hypervisor: return self.hypervisor hypervisor = self.module.params.get('hypervisor') hypervisors = self.cs.listHypervisors() # use the first hypervisor if no hypervisor param given if not hypervisor: self.hypervisor = hypervisors['hypervisor'][0]['name'] return self.hypervisor for h in hypervisors['hypervisor']: if hypervisor.lower() == h['name'].lower(): self.hypervisor = h['name'] return self.hypervisor self.module.fail_json(msg="Hypervisor '%s' not found" % hypervisor) def _poll_job(self, job=None, key=None): if 'jobid' in job: while True: res = self.cs.queryAsyncJobResult(jobid=job['jobid']) if res['jobstatus'] != 0: if 'jobresult' in res and key is not None and key in res[ 'jobresult']: job = res['jobresult'][key] break time.sleep(2) return job
class AnsibleCloudStack: def __init__(self, module): if not has_lib_cs: module.fail_json(msg="python library cs required: pip install cs") self.module = module self._connect() self.project = None self.ip_address = None self.zone = None self.vm = None self.os_type = None self.hypervisor = None self.capabilities = None def _connect(self): api_key = self.module.params.get('api_key') api_secret = self.module.params.get('secret_key') api_url = self.module.params.get('api_url') api_http_method = self.module.params.get('api_http_method') if api_key and api_secret and api_url: self.cs = CloudStack( endpoint=api_url, key=api_key, secret=api_secret, method=api_http_method ) else: self.cs = CloudStack(**read_config()) def _has_changed(self, want_dict, current_dict, only_keys=None): for key, value in want_dict.iteritems(): # Optionally limit by a list of keys if only_keys and key not in only_keys: continue; if key in current_dict: # API returns string for int in some cases, just to make sure if isinstance(value, int): current_dict[key] = int(current_dict[key]) elif isinstance(value, str): current_dict[key] = str(current_dict[key]) # Only need to detect a singe change, not every item if value != current_dict[key]: return True return False def _get_by_key(self, key=None, my_dict={}): if key: if key in my_dict: return my_dict[key] self.module.fail_json(msg="Something went wrong: %s not found" % key) return my_dict # TODO: for backward compatibility only, remove if not used anymore def get_project_id(self): return self.get_project(key='id') def get_project(self, key=None): if self.project: return self._get_by_key(key, self.project) project = self.module.params.get('project') if not project: return None projects = self.cs.listProjects(listall=True) if projects: for p in projects['project']: if project in [ p['name'], p['displaytext'], p['id'] ]: self.project = p return self._get_by_key(key, self.project) self.module.fail_json(msg="project '%s' not found" % project) # TODO: for backward compatibility only, remove if not used anymore def get_ip_address_id(self): return self.get_ip_address(key='id') def get_ip_address(self, key=None): if self.ip_address: return self._get_by_key(key, self.ip_address) ip_address = self.module.params.get('ip_address') if not ip_address: self.module.fail_json(msg="IP address param 'ip_address' is required") args = {} args['ipaddress'] = ip_address args['projectid'] = self.get_project(key='id') ip_addresses = self.cs.listPublicIpAddresses(**args) if not ip_addresses: self.module.fail_json(msg="IP address '%s' not found" % args['ipaddress']) self.ip_address = ip_addresses['publicipaddress'][0] return self._get_by_key(key, self.ip_address) # TODO: for backward compatibility only, remove if not used anymore def get_vm_id(self): return self.get_vm(key='id') def get_vm(self, key=None): if self.vm: return self._get_by_key(key, self.vm) vm = self.module.params.get('vm') if not vm: self.module.fail_json(msg="Virtual machine param 'vm' is required") args = {} args['projectid'] = self.get_project(key='id') args['zoneid'] = self.get_zone(key='id') vms = self.cs.listVirtualMachines(**args) if vms: for v in vms['virtualmachine']: if vm in [ v['name'], v['displayname'], v['id'] ]: self.vm = v return self._get_by_key(key, self.vm) self.module.fail_json(msg="Virtual machine '%s' not found" % vm) # TODO: for backward compatibility only, remove if not used anymore def get_zone_id(self): return self.get_zone(key='id') def get_zone(self, key=None): if self.zone: return self._get_by_key(key, self.zone) zone = self.module.params.get('zone') zones = self.cs.listZones() # use the first zone if no zone param given if not zone: self.zone = zones['zone'][0] return self._get_by_key(key, self.zone) if zones: for z in zones['zone']: if zone in [ z['name'], z['id'] ]: self.zone = z return self._get_by_key(key, self.zone) self.module.fail_json(msg="zone '%s' not found" % zone) # TODO: for backward compatibility only, remove if not used anymore def get_os_type_id(self): return self.get_os_type(key='id') def get_os_type(self, key=None): if self.os_type: return self._get_by_key(key, self.zone) os_type = self.module.params.get('os_type') if not os_type: return None os_types = self.cs.listOsTypes() if os_types: for o in os_types['ostype']: if os_type in [ o['description'], o['id'] ]: self.os_type = o return self._get_by_key(key, self.os_type) self.module.fail_json(msg="OS type '%s' not found" % os_type) def get_hypervisor(self): if self.hypervisor: return self.hypervisor hypervisor = self.module.params.get('hypervisor') hypervisors = self.cs.listHypervisors() # use the first hypervisor if no hypervisor param given if not hypervisor: self.hypervisor = hypervisors['hypervisor'][0]['name'] return self.hypervisor for h in hypervisors['hypervisor']: if hypervisor.lower() == h['name'].lower(): self.hypervisor = h['name'] return self.hypervisor self.module.fail_json(msg="Hypervisor '%s' not found" % hypervisor) def get_capabilities(self, key=None): if self.capabilities: return self._get_by_key(key, self.capabilities) capabilities = self.cs.listCapabilities() self.capabilities = capabilities['capability'] return self._get_by_key(key, self.capabilities) def _poll_job(self, job=None, key=None): if 'jobid' in job: while True: res = self.cs.queryAsyncJobResult(jobid=job['jobid']) if res['jobstatus'] != 0 and 'jobresult' in res: if 'errortext' in res['jobresult']: self.module.fail_json(msg="Failed: '%s'" % res['jobresult']['errortext']) if key and key in res['jobresult']: job = res['jobresult'][key] break time.sleep(2) return job
class AnsibleCloudStack: def __init__(self, module): if not has_lib_cs: module.fail_json(msg="python library cs required: pip install cs") self.module = module self._connect() self.project_id = None self.ip_address_id = None self.zone_id = None self.vm_id = None self.os_type_id = None self.hypervisor = None def _connect(self): api_key = self.module.params.get('api_key') api_secret = self.module.params.get('api_secret') api_url = self.module.params.get('api_url') api_http_method = self.module.params.get('api_http_method') if api_key and api_secret and api_url: self.cs = CloudStack( endpoint=api_url, key=api_key, secret=api_secret, method=api_http_method ) else: self.cs = CloudStack(**read_config()) def get_project_id(self): if self.project_id: return self.project_id project = self.module.params.get('project') if not project: return None projects = self.cs.listProjects() if projects: for p in projects['project']: if project in [ p['name'], p['displaytext'], p['id'] ]: self.project_id = p['id'] return self.project_id self.module.fail_json(msg="project '%s' not found" % project) def get_ip_address_id(self): if self.ip_address_id: return self.ip_address_id ip_address = self.module.params.get('ip_address') if not ip_address: self.module.fail_json(msg="IP address param 'ip_address' is required") args = {} args['ipaddress'] = ip_address args['projectid'] = self.get_project_id() ip_addresses = self.cs.listPublicIpAddresses(**args) if not ip_addresses: self.module.fail_json(msg="IP address '%s' not found" % args['ipaddress']) self.ip_address_id = ip_addresses['publicipaddress'][0]['id'] return self.ip_address_id def get_vm_id(self): if self.vm_id: return self.vm_id vm = self.module.params.get('vm') if not vm: self.module.fail_json(msg="Virtual machine param 'vm' is required") args = {} args['projectid'] = self.get_project_id() vms = self.cs.listVirtualMachines(**args) if vms: for v in vms['virtualmachine']: if vm in [ v['name'], v['displayname'], v['id'] ]: self.vm_id = v['id'] return self.vm_id self.module.fail_json(msg="Virtual machine '%s' not found" % vm) def get_zone_id(self): if self.zone_id: return self.zone_id zone = self.module.params.get('zone') zones = self.cs.listZones() # use the first zone if no zone param given if not zone: self.zone_id = zones['zone'][0]['id'] return self.zone_id if zones: for z in zones['zone']: if zone in [ z['name'], z['id'] ]: self.zone_id = z['id'] return self.zone_id self.module.fail_json(msg="zone '%s' not found" % zone) def get_os_type_id(self): if self.os_type_id: return self.os_type_id os_type = self.module.params.get('os_type') if not os_type: return None os_types = self.cs.listOsTypes() if os_types: for o in os_types['ostype']: if os_type in [ o['description'], o['id'] ]: self.os_type_id = o['id'] return self.os_type_id self.module.fail_json(msg="OS type '%s' not found" % os_type) def get_hypervisor(self): if self.hypervisor: return self.hypervisor hypervisor = self.module.params.get('hypervisor') hypervisors = self.cs.listHypervisors() # use the first hypervisor if no hypervisor param given if not hypervisor: self.hypervisor = hypervisors['hypervisor'][0]['name'] return self.hypervisor for h in hypervisors['hypervisor']: if hypervisor.lower() == h['name'].lower(): self.hypervisor = h['name'] return self.hypervisor self.module.fail_json(msg="Hypervisor '%s' not found" % hypervisor) def _poll_job(self, job=None, key=None): if 'jobid' in job: while True: res = self.cs.queryAsyncJobResult(jobid=job['jobid']) if res['jobstatus'] != 0 and 'jobresult' in res: if 'errortext' in res['jobresult']: self.module.fail_json(msg="Failed: '%s'" % res['jobresult']['errortext']) if key and key in res['jobresult']: job = res['jobresult'][key] break time.sleep(2) return job