def remove_eula_from_device(self): params = dict(command='run', utilCmdArgs='/LICENSE.F5') uri = "https://{0}:{1}/mgmt/tm/util/bash".format( self.client.provider['server'], self.client.provider['server_port']) resp = self.client.api.post(uri, json=params) try: response = resp.json() except ValueError as ex: raise F5ModuleError(str(ex)) if 'code' in response and response['code'] in [400, 403]: if 'message' in response: raise F5ModuleError(response['message']) else: raise F5ModuleError(resp.content) if 'commandResult' in response: if 'No such file or directory' in response['commandResult']: return True else: raise F5ModuleError(response['commandResult']) return True
def _get_cert_references(self): result = dict() uri = "https://{0}:{1}/mgmt/cm/adc-core/working-config/sys/file/ssl-cert/".format( self.client.provider['server'], self.client.provider['server_port']) resp = self.client.api.get(uri) try: response = resp.json() except ValueError as ex: raise F5ModuleError(str(ex)) for cert in response['items']: key = fq_name(cert['partition'], cert['name']) result[key] = cert['selfLink'] return result
def destination(self): if self.want.ip is None and self.want.port is None: return None if self.want.port is None: self.want.update({'port': self.have.port}) if self.want.ip is None: self.want.update({'ip': self.have.ip}) if self.want.port in [None, '*'] and self.want.ip != '*': raise F5ModuleError( "Specifying an IP address requires that a port number be specified" ) if self.want.destination != self.have.destination: return self.want.destination
def exists(self): uri = "https://{0}:{1}/mgmt/tm/vcmp/guest/{2}".format( self.client.provider['server'], self.client.provider['server_port'], self.want.name ) resp = self.client.api.get(uri) try: response = resp.json() except ValueError as ex: raise F5ModuleError(str(ex)) if resp.status == 404 or 'code' in response and response['code'] == 404: return False if resp.status in [200, 201] or 'code' in response and response['code'] in [200, 201]: return True errors = [401, 403, 409, 500, 501, 502, 503, 504] if resp.status in errors or 'code' in response and response['code'] in errors: if 'message' in response: raise F5ModuleError(response['message']) else: raise F5ModuleError(resp.content)
def monitors(self): if self._values['monitors'] is None: return None if len(self._values['monitors']) == 1 and self._values['monitors'][0] == '': return 'default' monitors = [fq_name(self.partition, x) for x in self.monitors_list] if self.availability_requirement_type == 'at_least': if self.at_least > len(self.monitors_list): raise F5ModuleError( "The 'at_least' value must not exceed the number of 'monitors'." ) monitors = ' '.join(monitors) result = 'min {0} of {{ {1} }}'.format(self.at_least, monitors) elif self.availability_requirement_type == 'require': monitors = ' '.join(monitors) if self.number_of_probes > self.number_of_probers: raise F5ModuleError( "The 'number_of_probes' must not exceed the 'number_of_probers'." ) result = 'require {0} from {1} {{ {2} }}'.format(self.number_of_probes, self.number_of_probers, monitors) else: result = ' and '.join(monitors).strip() return result
def mgmt_tuple(self): Destination = namedtuple('Destination', ['ip', 'subnet']) try: parts = self._values['mgmt_address'].split('/') if len(parts) == 2: result = Destination(ip=parts[0], subnet=parts[1]) elif len(parts) < 2: result = Destination(ip=parts[0], subnet=None) else: raise F5ModuleError( "The provided mgmt_address is malformed." ) except ValueError: result = Destination(ip=None, subnet=None) return result
def create_on_device(self): params = self.want.api_params() payload = dict(name=self.want.name, partition=self.want.partition, **params) uri = "https://{0}:{1}/mgmt/tm/ltm/policy/".format( self.client.provider['server'], self.client.provider['server_port'], ) resp = self.client.api.post(uri, json=payload) try: response = resp.json() except ValueError as ex: raise F5ModuleError(str(ex)) if 'code' in response and response['code'] in [400, 403]: if 'message' in response: raise F5ModuleError(response['message']) else: raise F5ModuleError(resp.content) self._upsert_policy_rules_on_device() return True
def device_reference(self): if not self.managed: return None if self.device_is_address: # This range lookup is how you do lookups for single IP addresses. Weird. filter = "address+eq+'{0}...{0}'".format(self.device) elif self.device_is_name: filter = "hostname+eq+'{0}'".format(self.device) elif self.device_is_id: filter = "uuid+eq+'{0}'".format(self.device) else: raise F5ModuleError("Unknown device format '{0}'".format( self.device)) uri = "https://{0}:{1}/mgmt/shared/resolver/device-groups/cm-bigip-allBigIpDevices/devices/" \ "?$filter={2}&$top=1".format(self.client.provider['server'], self.client.provider['server_port'], filter) resp = self.client.api.get(uri) try: response = resp.json() except ValueError as ex: raise F5ModuleError(str(ex)) if resp.status == 200 and response['totalItems'] == 0: raise F5ModuleError( "No device with the specified address was found.") elif 'code' in response and response['code'] == 400: if 'message' in response: raise F5ModuleError(response['message']) else: raise F5ModuleError(resp._content) id = response['items'][0]['uuid'] result = dict( link= 'https://localhost/mgmt/shared/resolver/device-groups/cm-bigip-allBigIpDevices/devices/{0}' .format(id)) return result
def addresses(self): if self._values['addresses'] is None: return None result = [] for x in self._values['addresses']: if is_valid_ip(x): result.append(str(ip_address(u'{0}'.format(x)))) elif is_valid_ip_interface(x): result.append(str(ip_interface(u'{0}'.format(x)))) else: raise F5ModuleError( "Address {0} must be either an IPv4 or IPv6 address or network." .format(x)) result = sorted(result) return result
def inline_import(self): params = self.changes.api_params() uri = "https://{0}:{1}/mgmt/tm/asm/tasks/import-policy/".format( self.client.provider['server'], self.client.provider['server_port'], ) if self.want.force: params.update(dict(policyReference={'link': self._get_policy_link()})) params.pop('name') resp = self.client.api.post(uri, json=params) try: response = resp.json() except ValueError as ex: raise F5ModuleError(str(ex)) if 'code' in response and response['code'] in [400, 403]: if 'message' in response: raise F5ModuleError(response['message']) else: raise F5ModuleError(resp.content) return response['id']
def _policy_exists(self): uri = 'https://{0}:{1}/mgmt/tm/asm/policies/'.format( self.client.provider['server'], self.client.provider['server_port'], ) query = "?$filter=contains(name,'{0}')+and+contains(partition,'{1}')&$select=name,partition".format( self.want.name, self.want.partition) resp = self.client.api.get(uri + query) try: response = resp.json() except ValueError as ex: raise F5ModuleError(str(ex)) if 'items' in response and response['items'] != []: return True return False
def pools(self): result = [] if self._values['pools'] is None: return None for item in self._values['pools']: pool = dict() if 'name' not in item: raise F5ModuleError( "'name' is a required key for items in the list of pools." ) if 'ratio' in item: pool['ratio'] = item['ratio'] pool['name'] = fq_name(self.partition, item['name']) result.append(pool) return result
def _handle_forward_action(self, action, item): """Handle the nuances of the forwarding type Right now there is only a single type of forwarding that can be done. As that functionality expands, so-to will the behavior of this, and other, methods. Therefore, do not be surprised that the logic here is so rigid. It's deliberate. :param action: :param item: :return: """ action['type'] = 'forward' if 'pool' not in item: raise F5ModuleError( "A 'pool' must be specified when the 'forward' type is used.") action['pool'] = self._fqdn_name(item['pool'])
def _discovery_constraints(self): if self.want.virtual_server_discovery is None: virtual_server_discovery = self.have.virtual_server_discovery else: virtual_server_discovery = self.want.virtual_server_discovery if self.want.link_discovery is None: link_discovery = self.have.link_discovery else: link_discovery = self.want.link_discovery if link_discovery in ['enabled', 'enabled-no-delete' ] and virtual_server_discovery == 'disabled': raise F5ModuleError( "Virtual server discovery must be enabled if link discovery is enabled" )
def _handle_redirect_action(self, action, item): """Handle the nuances of the redirect type :param action: :param item: :return: """ action['type'] = 'redirect' if 'location' not in item: raise F5ModuleError( "A 'location' must be specified when the 'redirect' type is used." ) action.update( location=item['location'], httpReply=True, )
def route_domain(self): if self._values['route_domain'] is None: return None try: return int(self._values['route_domain']) except ValueError: try: rd = self.client.api.tm.net.route_domains.route_domain.load( name=self._values['route_domain'], partition=self.partition ) return int(rd.id) except iControlUnexpectedHTTPError: raise F5ModuleError( "The specified 'route_domain' was not found." )
def log_publisher(self): if self.want.log_publisher is None: return None if self.want.log_publisher == '' and self.have.log_publisher in [ None, 'none' ]: return None if self.want.log_publisher == '': if self.want.log_profile is None and self.have.log_profile not in [ None, 'none' ]: raise F5ModuleError( "The log_publisher cannot be removed if log_profile is defined on device." ) if self.want.log_publisher != self.have.log_publisher: return self.want.log_publisher
def __enter__(self): uri = "https://{0}:{1}/mgmt/tm/transaction/".format( self.client.provider['server'], self.client.provider['server_port']) resp = self.client.api.post(uri, json={}) if resp.status not in [200]: raise Exception try: response = resp.json() except ValueError as ex: raise F5ModuleError(str(ex)) self.transid = response['transId'] self.client.api.request.headers[ 'X-F5-REST-Coordination-Id'] = self.transid return self.client
def exists(self): uri = "https://{0}:{1}/mgmt/tm/asm/policies/".format( self.client.provider['server'], self.client.provider['server_port'], ) resp = self.client.api.get(uri) try: response = resp.json() except ValueError as ex: raise F5ModuleError(str(ex)) if any(p['name'] == self.want.name and p['partition'] == self.want.partition for p in response['items']): return True return False
def create_on_device(self): remote_path = "/var/local/ucs" tpath_name = '/var/config/rest/downloads' upload = self.client.api.shared.file_transfer.uploads try: upload.upload_file(self.want.ucs) except IOError as ex: raise F5ModuleError(str(ex)) self.client.api.tm.util.unix_mv.exec_cmd( 'run', utilCmdArgs='{0}/{2} {1}/{2}'.format(tpath_name, remote_path, self.want.basename)) return True
def create_on_device(self): remote_path = "/var/config/rest/downloads/{0}".format(self.want.name) load_command = 'tmsh load sys application template {0}'.format( remote_path) template = StringIO(self.want.content) upload = self.client.api.shared.file_transfer.uploads upload.upload_stringio(template, self.want.name) output = self.client.api.tm.util.bash.exec_cmd( 'run', utilCmdArgs='-c "{0}"'.format(load_command)) if hasattr(output, 'commandResult'): result = output.commandResult if 'Syntax Error' in result: raise F5ModuleError(output.commandResult)
def _handle_enable_action(self, action, item): """Handle the nuances of the enable type :param action: :param item: :return: """ action['type'] = 'enable' if 'asm_policy' not in item: raise F5ModuleError( "An 'asm_policy' must be specified when the 'enable' type is used." ) action.update(dict( policy=fq_name(self.partition, item['asm_policy']), asm=True ))
def remove(self): if self.module.check_mode: return True self.remove_from_device() self._wait_for_module_provisioning() # For vCMP, because it has to reboot, we also wait for mcpd to become available # before "moving on", or else the REST API would not be available and subsequent # Tasks would fail. if self.want.module == 'vcmp': self._wait_for_reboot() self._wait_for_module_provisioning() if self.exists(): raise F5ModuleError("Failed to de-provision the module") return True
def ssl_protocols(self): if self._values['ssl_protocols'] is None: return None if isinstance(self._values['ssl_protocols'], string_types): protocols = self._values['ssl_protocols'].strip() else: protocols = self._values['ssl_protocols'] if not protocols: raise F5ModuleError("ssl_protocols may not be set to 'none'") if protocols == 'default': protocols = ' '.join(Parameters._protocols.split(' ')) elif isinstance(protocols, string_types): protocols = ' '.join(protocols.split(' ')) else: protocols = ' '.join(protocols) return protocols
def ssl_cipher_suite(self): if self._values['ssl_cipher_suite'] is None: return None if isinstance(self._values['ssl_cipher_suite'], string_types): ciphers = self._values['ssl_cipher_suite'].strip() else: ciphers = self._values['ssl_cipher_suite'] if not ciphers: raise F5ModuleError("ssl_cipher_suite may not be set to 'none'") if ciphers == 'default': ciphers = ':'.join(Parameters._ciphers.split(':')) elif isinstance(self._values['ssl_cipher_suite'], string_types): ciphers = ':'.join(ciphers.split(':')) else: ciphers = ':'.join(ciphers) return ciphers
def exec_module(self): if not self.gtm_provisioned(): raise F5ModuleError("GTM must be provisioned to use this module.") if 'all' in self.want.include: names = ['pool', 'wide_ip', 'server'] else: names = self.want.include managers = [self.get_manager(name) for name in names] result = self.execute_managers(managers) if result: result['changed'] = True else: result['changed'] = False self._announce_deprecations() return result
def exec_module(self): if not module_provisioned(self.client, 'asm'): raise F5ModuleError( "ASM must be provisioned to use this module." ) result = dict() changed = self.policy_import() reportable = ReportableChanges(params=self.changes.to_return()) changes = reportable.to_return() result.update(**changes) result.update(dict(changed=changed)) self._announce_deprecations(result) return result
def remove_virtual_disk_from_device(self): check = '{0}'.format(self.have.virtual_disk) response = self.get_virtual_disks_on_device() for resource in response['items']: if resource['name'].startswith(check): uri = "https://{0}:{1}/mgmt/tm/vcmp/virtual-disk/{2}".format( self.client.provider['server'], self.client.provider['server_port'], resource['name'].replace('/', '~')) response = self.client.api.delete(uri) if response.status == 200: continue else: raise F5ModuleError(response.content) return True
def destination(self): if self._values['destination'] is None: return None if self._values['destination'].startswith('default'): self._values['destination'] = '0.0.0.0/0' if self._values['destination'].startswith('default-inet6'): self._values['destination'] = '::/0' try: ip = ip_network(u'%s' % str(self.destination_ip)) if self.route_domain: return '{0}%{1}/{2}'.format(str(ip.network_address), self.route_domain, ip.prefixlen) else: return '{0}/{1}'.format(str(ip.network_address), ip.prefixlen) except ValueError: raise F5ModuleError( "The provided destination is not an IP address")
def create(self): self._set_changed_options() if self.changes.version is None: raise F5ModuleError( "The 'version' parameter is required when creating a new IKE peer." ) if self.changes.phase1_auth_method is None: self.changes.update({'phase1_auth_method': 'rsa-signature'}) if self.changes.phase1_cert is None: self.changes.update({'phase1_cert': 'default.crt'}) if self.changes.phase1_key is None: self.changes.update({'phase1_key': 'default.key'}) if self.module.check_mode: return True self.create_on_device() return True