# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """Compute v2 API Library""" from keystoneauth1 import exceptions as ksa_exceptions from osc_lib.api import api from osc_lib import exceptions from osc_lib.i18n import _ # TODO(dtroyer): Mingrate this to osc-lib class InvalidValue(Exception): """An argument value is not valid: wrong type, out of range, etc""" message = "Supplied value is not valid" class APIv2(api.BaseAPI): """Compute v2 API""" def __init__(self, **kwargs): super(APIv2, self).__init__(**kwargs) # Overrides def _check_integer(self, value, msg=None): """Attempt to convert value to an integer Raises InvalidValue on failure :param value: Convert this to an integer. None is converted to 0 (zero). :param msg: An alternate message for the exception, must include exactly one substitution to receive the attempted value. """ if value is None: return 0 try: value = int(value) except (TypeError, ValueError): if not msg: msg = "%s is not an integer" % value raise InvalidValue(msg) return value # TODO(dtroyer): Override find() until these fixes get into an osc-lib # minimum release def find( self, path, value=None, attr=None, ): """Find a single resource by name or ID :param string path: The API-specific portion of the URL path :param string value: search expression (required, really) :param string attr: name of attribute for secondary search """ try: ret = self._request('GET', "/%s/%s" % (path, value)).json() if isinstance(ret, dict): # strip off the enclosing dict key = list(ret.keys())[0] ret = ret[key] except ( ksa_exceptions.NotFound, ksa_exceptions.BadRequest, ): kwargs = {attr: value} try: ret = self.find_one(path, **kwargs) except ksa_exceptions.NotFound: msg = _("%s not found") % value raise exceptions.NotFound(msg) return ret # Flaoting IPs def floating_ip_create( self, pool=None, ): """Create a new floating ip https://developer.openstack.org/api-ref/compute/#create-allocate-floating-ip-address :param pool: Name of floating IP pool """ url = "/os-floating-ips" try: return self.create( url, json={'pool': pool}, )['floating_ip'] except ( ksa_exceptions.NotFound, ksa_exceptions.BadRequest, ): msg = _("%s not found") % pool raise exceptions.NotFound(msg) def floating_ip_delete( self, floating_ip_id=None, ): """Delete a floating IP https://developer.openstack.org/api-ref/compute/#delete-deallocate-floating-ip-address :param string security_group: Floating IP ID """ url = "/os-floating-ips" if floating_ip_id is not None: return self.delete('/%s/%s' % (url, floating_ip_id)) return None def floating_ip_find( self, floating_ip=None, ): """Return a security group given name or ID https://developer.openstack.org/api-ref/compute/#list-floating-ip-addresses :param string floating_ip: Floating IP address :returns: A dict of the floating IP attributes """ url = "/os-floating-ips" return self.find( url, attr='ip', value=floating_ip, ) def floating_ip_list( self, ): """Get floating IPs https://developer.openstack.org/api-ref/compute/#show-floating-ip-address-details :returns: list of floating IPs """ url = "/os-floating-ips" return self.list(url)["floating_ips"] # Floating IP Pools def floating_ip_pool_list( self, ): """Get floating IP pools https://developer.openstack.org/api-ref/compute/?expanded=#list-floating-ip-pools :returns: list of floating IP pools """ url = "/os-floating-ip-pools" return self.list(url)["floating_ip_pools"] # Networks def network_create( self, name=None, subnet=None, share_subnet=None, ): """Create a new network https://developer.openstack.org/api-ref/compute/#create-network :param string name: Network label (required) :param integer subnet: Subnet for IPv4 fixed addresses in CIDR notation (required) :param integer share_subnet: Shared subnet between projects, True or False :returns: A dict of the network attributes """ url = "/os-networks" params = { 'label': name, 'cidr': subnet, } if share_subnet is not None: params['share_address'] = share_subnet return self.create( url, json={'network': params}, )['network'] def network_delete( self, network=None, ): """Delete a network https://developer.openstack.org/api-ref/compute/#delete-network :param string network: Network name or ID """ url = "/os-networks" network = self.find( url, attr='label', value=network, )['id'] if network is not None: return self.delete('/%s/%s' % (url, network)) return None def network_find( self, network=None, ): """Return a network given name or ID https://developer.openstack.org/api-ref/compute/#show-network-details :param string network: Network name or ID :returns: A dict of the network attributes """ url = "/os-networks" return self.find( url, attr='label', value=network, ) def network_list( self, ): """Get networks https://developer.openstack.org/api-ref/compute/#list-networks :returns: list of networks """ url = "/os-networks" return self.list(url)["networks"] # Security Groups def security_group_create( self, name=None, description=None, ): """Create a new security group https://developer.openstack.org/api-ref/compute/#create-security-group :param string name: Security group name :param integer description: Security group description """ url = "/os-security-groups" params = { 'name': name, 'description': description, } return self.create( url, json={'security_group': params}, )['security_group'] def security_group_delete( self, security_group=None, ): """Delete a security group https://developer.openstack.org/api-ref/compute/#delete-security-group :param string security_group: Security group name or ID """ url = "/os-security-groups" security_group = self.find( url, attr='name', value=security_group, )['id'] if security_group is not None: return self.delete('/%s/%s' % (url, security_group)) return None def security_group_find( self, security_group=None, ): """Return a security group given name or ID https://developer.openstack.org/api-ref/compute/#show-security-group-details :param string security_group: Security group name or ID :returns: A dict of the security group attributes """ url = "/os-security-groups" return self.find( url, attr='name', value=security_group, ) def security_group_list( self, limit=None, marker=None, search_opts=None, ): """Get security groups https://developer.openstack.org/api-ref/compute/#list-security-groups :param integer limit: query return count limit :param string marker: query marker :param search_opts: (undocumented) Search filter dict all_tenants: True|False - return all projects :returns: list of security groups names """ params = {} if search_opts is not None: params = dict((k, v) for (k, v) in search_opts.items() if v) if limit: params['limit'] = limit if marker: params['offset'] = marker url = "/os-security-groups" return self.list(url, **params)["security_groups"] def security_group_set( self, security_group=None, # name=None, # description=None, **params ): """Update a security group https://developer.openstack.org/api-ref/compute/#update-security-group :param string security_group: Security group name or ID TODO(dtroyer): Create an update method in osc-lib """ # Short-circuit no-op if params is None: return None url = "/os-security-groups" security_group = self.find( url, attr='name', value=security_group, ) if security_group is not None: for (k, v) in params.items(): # Only set a value if it is already present if k in security_group: security_group[k] = v return self._request( "PUT", "/%s/%s" % (url, security_group['id']), json={'security_group': security_group}, ).json()['security_group'] return None # Security Group Rules def security_group_rule_create( self, security_group_id=None, ip_protocol=None, from_port=None, to_port=None, remote_ip=None, remote_group=None, ): """Create a new security group rule https://developer.openstack.org/api-ref/compute/#create-security-group-rule :param string security_group_id: Security group ID :param ip_protocol: IP protocol, 'tcp', 'udp' or 'icmp' :param from_port: Source port :param to_port: Destination port :param remote_ip: Source IP address in CIDR notation :param remote_group: Remote security group """ url = "/os-security-group-rules" if ip_protocol.lower() not in ['icmp', 'tcp', 'udp']: raise InvalidValue( "%(s) is not one of 'icmp', 'tcp', or 'udp'" % ip_protocol ) params = { 'parent_group_id': security_group_id, 'ip_protocol': ip_protocol, 'from_port': self._check_integer(from_port), 'to_port': self._check_integer(to_port), 'cidr': remote_ip, 'group_id': remote_group, } return self.create( url, json={'security_group_rule': params}, )['security_group_rule'] def security_group_rule_delete( self, security_group_rule_id=None, ): """Delete a security group rule https://developer.openstack.org/api-ref/compute/#delete-security-group-rule :param string security_group_rule_id: Security group rule ID """ url = "/os-security-group-rules" if security_group_rule_id is not None: return self.delete('/%s/%s' % (url, security_group_rule_id)) return None