def network_driver_leave(): """Unbinds a Neutron Port to a network interface attached to a container. This function takes the following JSON data and delete the veth pair corresponding to the given info. :: { "NetworkID": string, "EndpointID": string } """ json_data = flask.request.get_json(force=True) app.logger.debug("Received JSON data {0} for" " /NetworkDriver.DeleteEndpoint" .format(json_data)) jsonschema.validate(json_data, schemata.LEAVE_SCHEMA) neutron_network_identifier = _make_net_identifier(json_data['NetworkID'], tags=app.tag) endpoint_id = json_data['EndpointID'] filtered_networks = _get_networks_by_identifier(neutron_network_identifier) if not filtered_networks: return flask.jsonify({ 'Err': "Neutron net associated with identifier {0} doesn't exit." .format(neutron_network_identifier) }) else: neutron_port_name = utils.get_neutron_port_name(endpoint_id) filtered_ports = _get_ports_by_attrs(name=neutron_port_name) if not filtered_ports: raise exceptions.NoResourceException( "The port doesn't exist for the name {0}" .format(neutron_port_name)) neutron_port = filtered_ports[0] try: stdout, stderr = binding.port_unbind(endpoint_id, neutron_port) app.logger.debug(stdout) if stderr: app.logger.error(stderr) except processutils.ProcessExecutionError: with excutils.save_and_reraise_exception(): app.logger.error(_LE( 'Could not unbind the Neutron port from the veth ' 'endpoint.')) except exceptions.VethDeletionFailure: with excutils.save_and_reraise_exception(): app.logger.error(_LE('Cleaning the veth pair up was failed.')) return flask.jsonify(const.SCHEMA['SUCCESS'])
def network_driver_leave(): """Unbinds a Neutron Port to a network interface attached to a container. This function takes the following JSON data and delete the veth pair corresponding to the given info. :: { "NetworkID": string, "EndpointID": string } """ json_data = flask.request.get_json(force=True) app.logger.debug("Received JSON data {0} for" " /NetworkDriver.DeleteEndpoint".format(json_data)) jsonschema.validate(json_data, schemata.LEAVE_SCHEMA) neutron_network_identifier = _make_net_identifier(json_data['NetworkID'], tags=app.tag) endpoint_id = json_data['EndpointID'] filtered_networks = _get_networks_by_identifier(neutron_network_identifier) if not filtered_networks: return flask.jsonify({ 'Err': "Neutron net associated with identifier {0} doesn't exit.".format( neutron_network_identifier) }) else: neutron_port_name = utils.get_neutron_port_name(endpoint_id) filtered_ports = _get_ports_by_attrs(name=neutron_port_name) if not filtered_ports: raise exceptions.NoResourceException( "The port doesn't exist for the name {0}".format( neutron_port_name)) neutron_port = filtered_ports[0] try: stdout, stderr = binding.port_unbind(endpoint_id, neutron_port) app.logger.debug(stdout) if stderr: app.logger.error(stderr) except processutils.ProcessExecutionError: with excutils.save_and_reraise_exception(): app.logger.error( _LE('Could not unbind the Neutron port from the veth ' 'endpoint.')) except exceptions.VethDeletionFailure: with excutils.save_and_reraise_exception(): app.logger.error(_LE('Cleaning the veth pair up was failed.')) return flask.jsonify(const.SCHEMA['SUCCESS'])
def ipam_release_pool(): """Deletes a new Neutron subnetpool from the given reuest. This function takes the following JSON data and delegates the subnetpool deletion to the Neutron client. :: { "PoolID": string } Then the following JSON response is returned. :: {} See the following link for more details about the spec: https://github.com/docker/libnetwork/blob/master/docs/ipam.md#releasepool # noqa """ json_data = flask.request.get_json(force=True) app.logger.debug("Received JSON data {0} for /IpamDriver.ReleasePool" .format(json_data)) jsonschema.validate(json_data, schemata.RELEASE_POOL_SCHEMA) pool_id = json_data['PoolID'] try: app.neutron.delete_subnetpool(pool_id) except n_exceptions.Conflict as ex: app.logger.info(_LI("The subnetpool with ID {0} is still in use." " It can't be deleted for now.").format(pool_id)) except n_exceptions.NeutronClientException as ex: app.logger.error(_LE("Error happend during deleting a " "Neutron subnetpool: {0}").format(ex)) raise return flask.jsonify(const.SCHEMA['SUCCESS'])
def make_json_error(ex): app.logger.error(_LE("Unexpected error happened: {0}").format(ex)) traceback.print_exc(file=sys.stderr) response = flask.jsonify({"Err": str(ex)}) response.status_code = w_exceptions.InternalServerError.code if isinstance(ex, w_exceptions.HTTPException): response.status_code = ex.code elif isinstance(ex, n_exceptions.NeutronClientException): response.status_code = ex.status_code elif isinstance(ex, jsonschema.ValidationError): response.status_code = w_exceptions.BadRequest.code content_type = 'application/vnd.docker.plugins.v1+json; charset=utf-8' response.headers['Content-Type'] = content_type return response
def _update_port(port, endpoint_id): port['name'] = utils.get_neutron_port_name(endpoint_id) try: response_port = app.neutron.update_port( port['id'], {'port': { 'name': port['name'], 'device_owner': const.DEVICE_OWNER, 'device_id': endpoint_id}}) except n_exceptions.NeutronClientException as ex: app.logger.error(_LE("Error happend during creating a " "Neutron port: {0}").format(ex)) raise return response_port['port']
def _cache_default_subnetpool_ids(app): """Caches IDs of the default subnetpools as app.DEFAULT_POOL_IDS.""" if not hasattr(app, 'DEFAULT_POOL_IDS'): default_subnetpool_id_set = set() try: subnetpool_names = SUBNET_POOLS_V4 + SUBNET_POOLS_V6 for subnetpool_name in subnetpool_names: subnetpools = app.neutron.list_subnetpools( name=subnetpool_name) for subnetpool in subnetpools['subnetpools']: default_subnetpool_id_set.add(subnetpool['id']) except n_exceptions.NeutronClientException as ex: app.logger.error(_LE("Error happened during retrieving the default" " subnet pools.").format(ex)) app.DEFAULT_POOL_IDS = frozenset(default_subnetpool_id_set)
def _cache_default_subnetpool_ids(app): """Caches IDs of the default subnetpools as app.DEFAULT_POOL_IDS.""" if not hasattr(app, 'DEFAULT_POOL_IDS'): default_subnetpool_id_set = set() try: subnetpool_names = SUBNET_POOLS_V4 + SUBNET_POOLS_V6 for subnetpool_name in subnetpool_names: subnetpools = app.neutron.list_subnetpools( name=subnetpool_name) for subnetpool in subnetpools['subnetpools']: default_subnetpool_id_set.add(subnetpool['id']) except n_exceptions.NeutronClientException as ex: app.logger.error( _LE("Error happened during retrieving the default" " subnet pools.").format(ex)) app.DEFAULT_POOL_IDS = frozenset(default_subnetpool_id_set)
def _update_port(port, endpoint_id): port['name'] = utils.get_neutron_port_name(endpoint_id) try: response_port = app.neutron.update_port( port['id'], { 'port': { 'name': port['name'], 'device_owner': const.DEVICE_OWNER, 'device_id': endpoint_id } }) except n_exceptions.NeutronClientException as ex: app.logger.error( _LE("Error happend during creating a " "Neutron port: {0}").format(ex)) raise return response_port['port']
def _create_port(endpoint_id, neutron_network_id, interface_mac, fixed_ips): port = { 'name': utils.get_neutron_port_name(endpoint_id), 'admin_state_up': True, 'network_id': neutron_network_id, 'device_owner': const.DEVICE_OWNER, 'device_id': endpoint_id, 'binding:host_id': utils.get_hostname(), 'fixed_ips': fixed_ips } if interface_mac: port['mac_address'] = interface_mac try: rcvd_port = app.neutron.create_port({'port': port}) except n_exceptions.NeutronClientException as ex: app.logger.error(_LE("Error happend during creating a" " Neutron port: {0}").format(ex)) raise return rcvd_port['port']
def _create_port(endpoint_id, neutron_network_id, interface_mac, fixed_ips): port = { 'name': utils.get_neutron_port_name(endpoint_id), 'admin_state_up': True, 'network_id': neutron_network_id, 'device_owner': const.DEVICE_OWNER, 'device_id': endpoint_id, 'binding:host_id': utils.get_hostname(), 'fixed_ips': fixed_ips } if interface_mac: port['mac_address'] = interface_mac try: rcvd_port = app.neutron.create_port({'port': port}) except n_exceptions.NeutronClientException as ex: app.logger.error( _LE("Error happend during creating a" " Neutron port: {0}").format(ex)) raise return rcvd_port['port']
def ipam_release_pool(): """Deletes a new Neutron subnetpool from the given reuest. This function takes the following JSON data and delegates the subnetpool deletion to the Neutron client. :: { "PoolID": string } Then the following JSON response is returned. :: {} See the following link for more details about the spec: https://github.com/docker/libnetwork/blob/master/docs/ipam.md#releasepool # noqa """ json_data = flask.request.get_json(force=True) app.logger.debug( "Received JSON data {0} for /IpamDriver.ReleasePool".format(json_data)) jsonschema.validate(json_data, schemata.RELEASE_POOL_SCHEMA) pool_id = json_data['PoolID'] try: app.neutron.delete_subnetpool(pool_id) except n_exceptions.Conflict as ex: app.logger.info( _LI("The subnetpool with ID {0} is still in use." " It can't be deleted for now.").format(pool_id)) except n_exceptions.NeutronClientException as ex: app.logger.error( _LE("Error happend during deleting a " "Neutron subnetpool: {0}").format(ex)) raise return flask.jsonify(const.SCHEMA['SUCCESS'])
def ipam_release_address(): """Deallocates the IP address in the given request. This function takes the following JSON data and remove the given IP address from the allocation_pool attribute of the subnet. :: { "PoolID": string "Address": string } Then the following response is returned. :: {} See the following link for more details about the spec: https://github.com/docker/libnetwork/blob/master/docs/ipam.md#releaseaddress # noqa """ json_data = flask.request.get_json(force=True) app.logger.debug( "Received JSON data {0} for /IpamDriver.ReleaseAddress".format( json_data)) jsonschema.validate(json_data, schemata.RELEASE_ADDRESS_SCHEMA) pool_id = json_data['PoolID'] rel_address = json_data['Address'] pools = _get_subnetpools_by_attrs(id=pool_id) if pools: pool = pools[0] prefixes = pool['prefixes'] for prefix in prefixes: cidr = netaddr.IPNetwork(prefix) subnet_network = str(cidr.network) subnet_cidr = '/'.join([subnet_network, str(cidr.prefixlen)]) else: raise exceptions.NoResourceException( "No subnetpools with id {0} is found.".format(pool_id)) # check if any subnet with matching cidr is present subnets = _get_subnets_by_attrs(cidr=subnet_cidr) if not len(subnets): raise exceptions.NoResourceException( "No subnet is found using pool {0} " "and pool_cidr {1}".format(pool_id, cidr)) subnet = subnets[0] cidr_address = netaddr.IPNetwork(rel_address) rcvd_fixed_ips = [] fixed_ip = {'subnet_id': subnet['id']} fixed_ip['ip_address'] = str(cidr_address.ip) rcvd_fixed_ips.append(fixed_ip) try: filtered_ports = [] all_ports = app.neutron.list_ports() for port in all_ports['ports']: if port['fixed_ips'] == rcvd_fixed_ips: filtered_ports.append(port) for port in filtered_ports: app.neutron.delete_port(port['id']) except n_exceptions.NeutronClientException as ex: app.logger.error( _LE("Error happend while fetching and deleting port, " "{0}").format(ex)) raise return flask.jsonify(const.SCHEMA['SUCCESS'])
def network_driver_delete_network(): """Delete the Neutron Network with name as the given NetworkID. This function takes the following JSON data and delegates the actual network deletion to the Neutron client. :: { "NetworkID": string } See the following link for more details about the spec: https://github.com/docker/libnetwork/blob/master/docs/remote.md#delete-network # noqa """ json_data = flask.request.get_json(force=True) app.logger.debug("Received JSON data {0} for" " /NetworkDriver.DeleteNetwork".format(json_data)) jsonschema.validate(json_data, schemata.NETWORK_DELETE_SCHEMA) container_net_id = json_data['NetworkID'] neutron_network_tags = utils.make_net_tags(container_net_id) existing_network_tags = neutron_network_tags + ',' existing_network_tags += const.KURYR_EXISTING_NEUTRON_NET try: existing_networks = _get_networks_by_attrs(tags=existing_network_tags) except n_exceptions.NeutronClientException as ex: app.logger.error( _LE("Error happened during listing " "Neutron networks: {0}").format(ex)) raise if existing_networks: app.logger.warn( _LW("Network is a pre existing Neutron network, " "not deleting in Neutron. removing tags: {0}").format( existing_network_tags)) neutron_net_id = existing_networks[0]['id'] _neutron_net_remove_tags(neutron_net_id, container_net_id) _neutron_net_remove_tag(neutron_net_id, const.KURYR_EXISTING_NEUTRON_NET) return flask.jsonify(const.SCHEMA['SUCCESS']) try: filtered_networks = _get_networks_by_attrs(tags=neutron_network_tags) except n_exceptions.NeutronClientException as ex: app.logger.error( _LE("Error happened during listing " "Neutron networks: {0}").format(ex)) raise if not filtered_networks: app.logger.warn( _LW("Network with tags {0} cannot be found").format( neutron_network_tags)) else: neutron_network_id = filtered_networks[0]['id'] filtered_subnets = _get_subnets_by_attrs(network_id=neutron_network_id) for subnet in filtered_subnets: try: subnetpool_id = subnet.get('subnetpool_id', None) _cache_default_subnetpool_ids(app) if subnetpool_id not in app.DEFAULT_POOL_IDS: # If the subnet to be deleted has any port, when some ports # are referring to the subnets in other words, # delete_subnet throws an exception, SubnetInUse that # extends Conflict. This can happen when the multiple # Docker endpoints are created with the same subnet CIDR # and it's totally the normal case. So we'd just log that # and continue to proceed. app.neutron.delete_subnet(subnet['id']) except n_exceptions.Conflict as ex: app.logger.error( _LE("Subnet, {0}, is in use. Network cant be deleted."). format(subnet['id'])) raise except n_exceptions.NeutronClientException as ex: app.logger.error( _LE("Error happened during deleting a " "Neutron subnets: {0}").format(ex)) raise try: app.neutron.delete_network(neutron_network_id) except n_exceptions.NeutronClientException as ex: app.logger.error( _LE("Error happened during deleting a " "Neutron network: {0}").format(ex)) raise app.logger.info( _LI("Deleted the network with ID {0} successfully").format( neutron_network_id)) return flask.jsonify(const.SCHEMA['SUCCESS'])
def network_driver_delete_network(): """Delete the Neutron Network with name as the given NetworkID. This function takes the following JSON data and delegates the actual network deletion to the Neutron client. :: { "NetworkID": string } See the following link for more details about the spec: https://github.com/docker/libnetwork/blob/master/docs/remote.md#delete-network # noqa """ json_data = flask.request.get_json(force=True) app.logger.debug("Received JSON data {0} for" " /NetworkDriver.DeleteNetwork".format(json_data)) jsonschema.validate(json_data, schemata.NETWORK_DELETE_SCHEMA) container_net_id = json_data['NetworkID'] neutron_network_identifier = _make_net_identifier(container_net_id, tags=app.tag) if app.tag: existing_network_identifier = neutron_network_identifier + ',' existing_network_identifier += const.KURYR_EXISTING_NEUTRON_NET try: existing_networks = _get_networks_by_identifier( existing_network_identifier) except n_exceptions.NeutronClientException as ex: app.logger.error(_LE("Error happened during listing " "Neutron networks: {0}").format(ex)) raise if existing_networks: app.logger.warn(_LW("Network is a pre existing Neutron network, " "not deleting in Neutron. removing tags: {0}") .format(existing_network_identifier)) neutron_net_id = existing_networks[0]['id'] _neutron_net_remove_tags(neutron_net_id, container_net_id) _neutron_net_remove_tag(neutron_net_id, const.KURYR_EXISTING_NEUTRON_NET) return flask.jsonify(const.SCHEMA['SUCCESS']) try: filtered_networks = _get_networks_by_identifier( neutron_network_identifier) except n_exceptions.NeutronClientException as ex: app.logger.error(_LE("Error happened during listing " "Neutron networks: {0}").format(ex)) raise if not filtered_networks: app.logger.warn(_LW("Network with identifier {0} cannot be found") .format(neutron_network_identifier)) else: neutron_network_id = filtered_networks[0]['id'] filtered_subnets = _get_subnets_by_attrs( network_id=neutron_network_id) for subnet in filtered_subnets: try: subnetpool_id = subnet.get('subnetpool_id', None) _cache_default_subnetpool_ids(app) if subnetpool_id not in app.DEFAULT_POOL_IDS: # If the subnet to be deleted has any port, when some ports # are referring to the subnets in other words, # delete_subnet throws an exception, SubnetInUse that # extends Conflict. This can happen when the multiple # Docker endpoints are created with the same subnet CIDR # and it's totally the normal case. So we'd just log that # and continue to proceed. app.neutron.delete_subnet(subnet['id']) except n_exceptions.Conflict as ex: app.logger.error(_LE( "Subnet, {0}, is in use. Network cant be deleted.").format( subnet['id'])) raise except n_exceptions.NeutronClientException as ex: app.logger.error(_LE("Error happened during deleting a " "Neutron subnets: {0}").format(ex)) raise try: app.neutron.delete_network(neutron_network_id) except n_exceptions.NeutronClientException as ex: app.logger.error(_LE("Error happened during deleting a " "Neutron network: {0}").format(ex)) raise app.logger.info(_LI("Deleted the network with ID {0} successfully") .format(neutron_network_id)) return flask.jsonify(const.SCHEMA['SUCCESS'])
def network_driver_create_network(): """Creates a new Neutron Network which name is the given NetworkID. This function takes the following JSON data and delegates the actual network creation to the Neutron client. libnetwork's NetworkID is used as the name of Network in Neutron. :: { "NetworkID": string, "IPv4Data" : [{ "AddressSpace": string, "Pool": ipv4-cidr-string, "Gateway" : ipv4-address, "AuxAddresses": { "<identifier1>" : "<ipv4-address1>", "<identifier2>" : "<ipv4-address2>", ... } }, ...], "IPv6Data" : [{ "AddressSpace": string, "Pool": ipv6-cidr-string, "Gateway" : ipv6-address, "AuxAddresses": { "<identifier1>" : "<ipv6-address1>", "<identifier2>" : "<ipv6-address2>", ... } }, ...], "Options": { ... } } See the following link for more details about the spec: https://github.com/docker/libnetwork/blob/master/docs/remote.md#create-network # noqa """ json_data = flask.request.get_json(force=True) app.logger.debug("Received JSON data {0} for" " /NetworkDriver.CreateNetwork".format(json_data)) jsonschema.validate(json_data, schemata.NETWORK_CREATE_SCHEMA) container_net_id = json_data['NetworkID'] neutron_network_name = utils.make_net_name(container_net_id, tags=app.tag) pool_cidr = json_data['IPv4Data'][0]['Pool'] gateway_ip = '' if 'Gateway' in json_data['IPv4Data'][0]: gateway_cidr = json_data['IPv4Data'][0]['Gateway'] gateway_ip = gateway_cidr.split('/')[0] app.logger.debug("gateway_cidr {0}, gateway_ip {1}" .format(gateway_cidr, gateway_ip)) neutron_uuid = None neutron_name = None options = json_data.get('Options') if options: generic_options = options.get(const.NETWORK_GENERIC_OPTIONS) if generic_options: neutron_uuid = generic_options.get(const.NEUTRON_UUID_OPTION) neutron_name = generic_options.get(const.NEUTRON_NAME_OPTION) if not neutron_uuid and not neutron_name: network = app.neutron.create_network( {'network': {'name': neutron_network_name, "admin_state_up": True}}) network_id = network['network']['id'] _neutron_net_add_tags(network['network']['id'], container_net_id, tags=app.tag) app.logger.info(_LI("Created a new network with name {0}" " successfully: {1}") .format(neutron_network_name, network)) else: try: if neutron_uuid: networks = _get_networks_by_attrs(id=neutron_uuid) else: networks = _get_networks_by_attrs(name=neutron_name) network_id = networks[0]['id'] except n_exceptions.NeutronClientException as ex: app.logger.error(_LE("Error happened during listing " "Neutron networks: {0}").format(ex)) raise if app.tag: _neutron_net_add_tags(network_id, container_net_id, tags=app.tag) _neutron_net_add_tag(network_id, const.KURYR_EXISTING_NEUTRON_NET) else: network = app.neutron.update_network( neutron_uuid, {'network': {'name': neutron_network_name}}) app.logger.info(_LI("Updated the network with new name {0}" " successfully: {1}") .format(neutron_network_name, network)) app.logger.info(_LI("Using existing network {0} successfully") .format(neutron_uuid)) cidr = netaddr.IPNetwork(pool_cidr) subnet_network = str(cidr.network) subnet_cidr = '/'.join([subnet_network, str(cidr.prefixlen)]) subnets = _get_subnets_by_attrs( network_id=network_id, cidr=subnet_cidr) if not subnets: new_subnets = [{ 'name': pool_cidr, 'network_id': network_id, 'ip_version': cidr.version, 'cidr': subnet_cidr, 'enable_dhcp': app.enable_dhcp, }] if gateway_ip: new_subnets[0]['gateway_ip'] = gateway_ip app.neutron.create_subnet({'subnets': new_subnets}) return flask.jsonify(const.SCHEMA['SUCCESS'])
def ipam_release_address(): """Deallocates the IP address in the given request. This function takes the following JSON data and remove the given IP address from the allocation_pool attribute of the subnet. :: { "PoolID": string "Address": string } Then the following response is returned. :: {} See the following link for more details about the spec: https://github.com/docker/libnetwork/blob/master/docs/ipam.md#releaseaddress # noqa """ json_data = flask.request.get_json(force=True) app.logger.debug("Received JSON data {0} for /IpamDriver.ReleaseAddress" .format(json_data)) jsonschema.validate(json_data, schemata.RELEASE_ADDRESS_SCHEMA) pool_id = json_data['PoolID'] rel_address = json_data['Address'] pools = _get_subnetpools_by_attrs(id=pool_id) if pools: pool = pools[0] prefixes = pool['prefixes'] for prefix in prefixes: cidr = netaddr.IPNetwork(prefix) subnet_network = str(cidr.network) subnet_cidr = '/'.join([subnet_network, str(cidr.prefixlen)]) else: raise exceptions.NoResourceException( "No subnetpools with id {0} is found." .format(pool_id)) # check if any subnet with matching cidr is present subnets = _get_subnets_by_attrs(cidr=subnet_cidr) if not len(subnets): raise exceptions.NoResourceException( "No subnet is found using pool {0} " "and pool_cidr {1}".format(pool_id, cidr)) subnet = subnets[0] cidr_address = netaddr.IPNetwork(rel_address) rcvd_fixed_ips = [] fixed_ip = {'subnet_id': subnet['id']} fixed_ip['ip_address'] = str(cidr_address.ip) rcvd_fixed_ips.append(fixed_ip) try: filtered_ports = [] all_ports = app.neutron.list_ports() for port in all_ports['ports']: if port['fixed_ips'] == rcvd_fixed_ips: filtered_ports.append(port) for port in filtered_ports: app.neutron.delete_port(port['id']) except n_exceptions.NeutronClientException as ex: app.logger.error(_LE("Error happend while fetching and deleting port, " "{0}").format(ex)) raise return flask.jsonify(const.SCHEMA['SUCCESS'])
def network_driver_join(): """Binds a Neutron Port to a network interface attached to a container. This function takes the following JSON data, creates a veth pair, put one end inside of the container and binds another end to the Neutron Port specified in the request. :: { "NetworkID": string, "EndpointID": string, "SandboxKey": string, "Options": { ... } } If the binding is succeeded, the following JSON response is returned.:: { "InterfaceName": { SrcName: string, DstPrefix: string }, "Gateway": string, "GatewayIPv6": string, "StaticRoutes": [{ "Destination": string, "RouteType": int, "NextHop": string, }, ...] } See the following link for more details about the spec: https://github.com/docker/libnetwork/blob/master/docs/remote.md#join # noqa """ json_data = flask.request.get_json(force=True) app.logger.debug("Received JSON data {0} for /NetworkDriver.Join" .format(json_data)) jsonschema.validate(json_data, schemata.JOIN_SCHEMA) neutron_network_identifier = _make_net_identifier(json_data['NetworkID'], tags=app.tag) endpoint_id = json_data['EndpointID'] filtered_networks = _get_networks_by_identifier(neutron_network_identifier) if not filtered_networks: return flask.jsonify({ 'Err': "Neutron net associated with identifier {0} doesn't exit." .format(neutron_network_identifier) }) else: neutron_network_id = filtered_networks[0]['id'] neutron_port_name = utils.get_neutron_port_name(endpoint_id) filtered_ports = _get_ports_by_attrs(name=neutron_port_name) if not filtered_ports: raise exceptions.NoResourceException( "The port doesn't exist for the name {0}" .format(neutron_port_name)) neutron_port = filtered_ports[0] all_subnets = _get_subnets_by_attrs(network_id=neutron_network_id) try: ifname, peer_name, (stdout, stderr) = binding.port_bind( endpoint_id, neutron_port, all_subnets) app.logger.debug(stdout) if stderr: app.logger.error(stderr) except exceptions.VethCreationFailure as ex: with excutils.save_and_reraise_exception(): app.logger.error(_LE('Preparing the veth ' 'pair was failed: {0}.') .format(ex)) except processutils.ProcessExecutionError: with excutils.save_and_reraise_exception(): app.logger.error(_LE( 'Could not bind the Neutron port to the veth endpoint.')) join_response = { "InterfaceName": { "SrcName": peer_name, "DstPrefix": config.CONF.binding.veth_dst_prefix }, "StaticRoutes": [] } for subnet in all_subnets: if subnet['ip_version'] == 4: join_response['Gateway'] = subnet.get('gateway_ip', '') else: join_response['GatewayIPv6'] = subnet.get('gateway_ip', '') host_routes = subnet.get('host_routes', []) for host_route in host_routes: static_route = { 'Destination': host_route['destination'] } if host_route.get('nexthop', None): static_route['RouteType'] = const.TYPES['NEXTHOP'] static_route['NextHop'] = host_route['nexthop'] else: static_route['RouteType'] = const.TYPES['CONNECTED'] join_response['StaticRoutes'].append(static_route) return flask.jsonify(join_response)
def ipam_request_pool(): """Creates a new Neutron subnetpool from the given request. This funciton takes the following JSON data and delegates the subnetpool creation to the Neutron client. :: { "AddressSpace": string "Pool": string "SubPool": string "Options": map[string]string "V6": bool } Then the following JSON response is returned. :: { "PoolID": string "Pool": string "Data": map[string]string } See the following link for more details about the spec: https://github.com/docker/libnetwork/blob/master/docs/ipam.md#requestpool # noqa """ json_data = flask.request.get_json(force=True) app.logger.debug( "Received JSON data {0} for /IpamDriver.RequestPool".format(json_data)) jsonschema.validate(json_data, schemata.REQUEST_POOL_SCHEMA) requested_pool = json_data['Pool'] requested_subpool = json_data['SubPool'] v6 = json_data['V6'] pool_id = '' subnet_cidr = '' if requested_pool: app.logger.info(_LI("Creating subnetpool with the given pool CIDR")) if requested_subpool: cidr = netaddr.IPNetwork(requested_subpool) else: cidr = netaddr.IPNetwork(requested_pool) subnet_cidr = _get_subnet_cidr_using_cidr(cidr) pool_name = utils.get_neutron_subnetpool_name(subnet_cidr) # Check if requested pool already exist pools = _get_subnetpools_by_attrs(name=pool_name) if pools: pool_id = pools[0]['id'] if not pools: new_subnetpool = { 'name': pool_name, 'default_prefixlen': cidr.prefixlen, 'prefixes': [subnet_cidr] } created_subnetpool_response = app.neutron.create_subnetpool( {'subnetpool': new_subnetpool}) pool = created_subnetpool_response['subnetpool'] pool_id = pool['id'] else: if v6: default_pool_list = SUBNET_POOLS_V6 else: default_pool_list = SUBNET_POOLS_V4 pool_name = default_pool_list[0] pools = _get_subnetpools_by_attrs(name=pool_name) if pools: pool = pools[0] pool_id = pool['id'] prefixes = pool['prefixes'] if len(prefixes) > 1: app.logger.warning( _LW("More than one prefixes present. " "Picking first one.")) cidr = netaddr.IPNetwork(prefixes[0]) subnet_cidr = _get_subnet_cidr_using_cidr(cidr) else: app.logger.error(_LE("Default neutron pools not found.")) req_pool_res = {'PoolID': pool_id, 'Pool': subnet_cidr} return flask.jsonify(req_pool_res)
def network_driver_create_network(): """Creates a new Neutron Network which name is the given NetworkID. This function takes the following JSON data and delegates the actual network creation to the Neutron client. libnetwork's NetworkID is used as the name of Network in Neutron. :: { "NetworkID": string, "IPv4Data" : [{ "AddressSpace": string, "Pool": ipv4-cidr-string, "Gateway" : ipv4-address, "AuxAddresses": { "<identifier1>" : "<ipv4-address1>", "<identifier2>" : "<ipv4-address2>", ... } }, ...], "IPv6Data" : [{ "AddressSpace": string, "Pool": ipv6-cidr-string, "Gateway" : ipv6-address, "AuxAddresses": { "<identifier1>" : "<ipv6-address1>", "<identifier2>" : "<ipv6-address2>", ... } }, ...], "Options": { ... } } See the following link for more details about the spec: https://github.com/docker/libnetwork/blob/master/docs/remote.md#create-network # noqa """ json_data = flask.request.get_json(force=True) app.logger.debug("Received JSON data {0} for" " /NetworkDriver.CreateNetwork".format(json_data)) jsonschema.validate(json_data, schemata.NETWORK_CREATE_SCHEMA) container_net_id = json_data['NetworkID'] neutron_network_name = utils.make_net_name(container_net_id) pool_cidr = json_data['IPv4Data'][0]['Pool'] gateway_ip = '' if 'Gateway' in json_data['IPv4Data'][0]: gateway_cidr = json_data['IPv4Data'][0]['Gateway'] gateway_ip = gateway_cidr.split('/')[0] app.logger.debug("gateway_cidr {0}, gateway_ip {1}".format( gateway_cidr, gateway_ip)) neutron_uuid = None neutron_name = None options = json_data.get('Options') if options: generic_options = options.get(const.NETWORK_GENERIC_OPTIONS) if generic_options: neutron_uuid = generic_options.get(const.NEUTRON_UUID_OPTION) neutron_name = generic_options.get(const.NEUTRON_NAME_OPTION) if not neutron_uuid and not neutron_name: network = app.neutron.create_network({ 'network': { 'name': neutron_network_name, "admin_state_up": True } }) network_id = network['network']['id'] _neutron_net_add_tags(network['network']['id'], container_net_id) app.logger.info( _LI("Created a new network with name {0}" " successfully: {1}").format(neutron_network_name, network)) else: try: if neutron_uuid: networks = _get_networks_by_attrs(id=neutron_uuid) else: networks = _get_networks_by_attrs(name=neutron_name) network_id = networks[0]['id'] except n_exceptions.NeutronClientException as ex: app.logger.error( _LE("Error happened during listing " "Neutron networks: {0}").format(ex)) raise _neutron_net_add_tags(network_id, container_net_id) _neutron_net_add_tag(network_id, const.KURYR_EXISTING_NEUTRON_NET) app.logger.info( _LI("Using existing network {0} successfully").format( neutron_uuid)) cidr = netaddr.IPNetwork(pool_cidr) subnet_network = str(cidr.network) subnet_cidr = '/'.join([subnet_network, str(cidr.prefixlen)]) subnets = _get_subnets_by_attrs(network_id=network_id, cidr=subnet_cidr) if not subnets: new_subnets = [{ 'name': pool_cidr, 'network_id': network_id, 'ip_version': cidr.version, 'cidr': subnet_cidr, 'enable_dhcp': app.enable_dhcp, }] if gateway_ip: new_subnets[0]['gateway_ip'] = gateway_ip app.neutron.create_subnet({'subnets': new_subnets}) return flask.jsonify(const.SCHEMA['SUCCESS'])
def network_driver_delete_network(): """Delete the Neutron Network with name as the given NetworkID. This function takes the following JSON data and delegates the actual network deletion to the Neutron client. :: { "NetworkID": string } See the following link for more details about the spec: https://github.com/docker/libnetwork/blob/master/docs/remote.md#delete-network # noqa """ json_data = flask.request.get_json(force=True) app.logger.debug("Received JSON data {0} for" " /NetworkDriver.DeleteNetwork".format(json_data)) jsonschema.validate(json_data, schemata.NETWORK_DELETE_SCHEMA) neutron_network_name = json_data['NetworkID'] try: filtered_networks = _get_networks_by_attrs(name=neutron_network_name) except n_exceptions.NeutronClientException as ex: app.logger.error(_LE("Error happened during listing " "Neutron networks: {0}").format(ex)) raise # We assume Neutron's Network names are not conflicted in Kuryr because # they are Docker IDs, 256 bits hashed values, which are rarely conflicted. # However, if there're multiple networks associated with the single # NetworkID, it raises DuplicatedResourceException and stops processes. # See the following doc for more details about Docker's IDs: # https://github.com/docker/docker/blob/master/docs/terms/container.md#container-ids # noqa if not filtered_networks: app.logger.warn("Network with name {0} cannot be found" .format(neutron_network_name)) else: neutron_network_id = filtered_networks[0]['id'] filtered_subnets = _get_subnets_by_attrs( network_id=neutron_network_id) for subnet in filtered_subnets: try: subnetpool_id = subnet.get('subnetpool_id', None) _cache_default_subnetpool_ids(app) if subnetpool_id not in app.DEFAULT_POOL_IDS: # If the subnet to be deleted has any port, when some ports # are referring to the subnets in other words, # delete_subnet throws an exception, SubnetInUse that # extends Conflict. This can happen when the multiple # Docker endpoints are created with the same subnet CIDR # and it's totally the normal case. So we'd just log that # and continue to proceed. app.neutron.delete_subnet(subnet['id']) except n_exceptions.Conflict as ex: app.logger.error(_LE( "Subnet, {0}, is in use. Network cant be deleted.").format( subnet['id'])) raise except n_exceptions.NeutronClientException as ex: app.logger.error(_LE("Error happened during deleting a " "Neutron subnets: {0}").format(ex)) raise try: app.neutron.delete_network(neutron_network_id) except n_exceptions.NeutronClientException as ex: app.logger.error(_LE("Error happened during deleting a " "Neutron network: {0}").format(ex)) raise app.logger.info(_LI("Deleted the network with ID {0} successfully") .format(neutron_network_id)) return flask.jsonify(constants.SCHEMA['SUCCESS'])
def network_driver_join(): """Binds a Neutron Port to a network interface attached to a container. This function takes the following JSON data, creates a veth pair, put one end inside of the container and binds another end to the Neutron Port specified in the request. :: { "NetworkID": string, "EndpointID": string, "SandboxKey": string, "Options": { ... } } If the binding is succeeded, the following JSON response is returned.:: { "InterfaceName": { SrcName: string, DstPrefix: string }, "Gateway": string, "GatewayIPv6": string, "StaticRoutes": [{ "Destination": string, "RouteType": int, "NextHop": string, }, ...] } See the following link for more details about the spec: https://github.com/docker/libnetwork/blob/master/docs/remote.md#join # noqa """ json_data = flask.request.get_json(force=True) app.logger.debug( "Received JSON data {0} for /NetworkDriver.Join".format(json_data)) jsonschema.validate(json_data, schemata.JOIN_SCHEMA) neutron_network_tags = utils.make_net_tags(json_data['NetworkID']) endpoint_id = json_data['EndpointID'] filtered_networks = _get_networks_by_attrs(tags=neutron_network_tags) if not filtered_networks: return flask.jsonify({ 'Err': "Neutron network associated with tags {0} doesn't exit.".format( neutron_network_tags) }) else: neutron_network_id = filtered_networks[0]['id'] neutron_port_name = utils.get_neutron_port_name(endpoint_id) filtered_ports = _get_ports_by_attrs(name=neutron_port_name) if not filtered_ports: raise exceptions.NoResourceException( "The port doesn't exist for the name {0}".format( neutron_port_name)) neutron_port = filtered_ports[0] all_subnets = _get_subnets_by_attrs(network_id=neutron_network_id) try: ifname, peer_name, (stdout, stderr) = binding.port_bind( endpoint_id, neutron_port, all_subnets) app.logger.debug(stdout) if stderr: app.logger.error(stderr) except exceptions.VethCreationFailure as ex: with excutils.save_and_reraise_exception(): app.logger.error( _LE('Preparing the veth ' 'pair was failed: {0}.').format(ex)) except processutils.ProcessExecutionError: with excutils.save_and_reraise_exception(): app.logger.error( _LE('Could not bind the Neutron port to the veth endpoint.' )) join_response = { "InterfaceName": { "SrcName": peer_name, "DstPrefix": config.CONF.binding.veth_dst_prefix }, "StaticRoutes": [] } for subnet in all_subnets: if subnet['ip_version'] == 4: join_response['Gateway'] = subnet.get('gateway_ip', '') else: join_response['GatewayIPv6'] = subnet.get('gateway_ip', '') host_routes = subnet.get('host_routes', []) for host_route in host_routes: static_route = {'Destination': host_route['destination']} if host_route.get('nexthop', None): static_route['RouteType'] = const.TYPES['NEXTHOP'] static_route['NextHop'] = host_route['nexthop'] else: static_route['RouteType'] = const.TYPES['CONNECTED'] join_response['StaticRoutes'].append(static_route) return flask.jsonify(join_response)
def ipam_request_address(): """Allocates the IP address in the given request. This function takes the following JSON data and add the given IP address in the allocation_pools attribute of the subnet. :: { "PoolID": string "Address": string "Options": map[string]string } Then the following response is returned. :: { "Address": string "Data": map[string]string } See the following link for more details about the spec: https://github.com/docker/libnetwork/blob/master/docs/ipam.md#requestaddress # noqa """ json_data = flask.request.get_json(force=True) app.logger.debug("Received JSON data {0} for /IpamDriver.RequestAddress" .format(json_data)) jsonschema.validate(json_data, schemata.REQUEST_ADDRESS_SCHEMA) pool_id = json_data['PoolID'] req_address = json_data['Address'] allocated_address = '' subnet_cidr = '' pool_prefix_len = '' pools = _get_subnetpools_by_attrs(id=pool_id) if pools: pool = pools[0] prefixes = pool['prefixes'] if len(prefixes) > 1: app.logger.warning(_LW("More than one prefixes present. Picking " "first one.")) for prefix in prefixes: cidr = netaddr.IPNetwork(prefix) pool_prefix_len = str(cidr.prefixlen) subnet_network = str(cidr.network) subnet_cidr = '/'.join([subnet_network, pool_prefix_len]) break else: raise exceptions.NoResourceException( "No subnetpools with id {0} is found." .format(pool_id)) # check if any subnet with matching cidr is present subnets = _get_subnets_by_attrs(cidr=subnet_cidr) if subnets: subnet = subnets[0] # allocating address for container port neutron_network_id = subnet['network_id'] try: port = { 'name': 'kuryr-unbound-port', 'admin_state_up': True, 'network_id': neutron_network_id, 'binding:host_id': utils.get_hostname(), } fixed_ips = port['fixed_ips'] = [] fixed_ip = {'subnet_id': subnet['id']} if req_address: fixed_ip['ip_address'] = req_address fixed_ips.append(fixed_ip) created_port_resp = app.neutron.create_port({'port': port}) created_port = created_port_resp['port'] allocated_address = created_port['fixed_ips'][0]['ip_address'] allocated_address = '/'.join( [allocated_address, str(cidr.prefixlen)]) except n_exceptions.NeutronClientException as ex: app.logger.error(_LE("Error happend during ip allocation on" "Neutron side: {0}").format(ex)) raise else: # Auxiliary address or gw_address is received at network creation time. # This address cannot be reserved with neutron at this time as subnet # is not created yet. In /NetworkDriver.CreateNetwork this address will # be reserved with neutron. if req_address: allocated_address = '/'.join([req_address, pool_prefix_len]) return flask.jsonify({'Address': allocated_address})
def ipam_request_address(): """Allocates the IP address in the given request. This function takes the following JSON data and add the given IP address in the allocation_pools attribute of the subnet. :: { "PoolID": string "Address": string "Options": map[string]string } Then the following response is returned. :: { "Address": string "Data": map[string]string } See the following link for more details about the spec: https://github.com/docker/libnetwork/blob/master/docs/ipam.md#requestaddress # noqa """ json_data = flask.request.get_json(force=True) app.logger.debug( "Received JSON data {0} for /IpamDriver.RequestAddress".format( json_data)) jsonschema.validate(json_data, schemata.REQUEST_ADDRESS_SCHEMA) pool_id = json_data['PoolID'] req_address = json_data['Address'] allocated_address = '' subnet_cidr = '' pool_prefix_len = '' pools = _get_subnetpools_by_attrs(id=pool_id) if pools: pool = pools[0] prefixes = pool['prefixes'] if len(prefixes) > 1: app.logger.warning( _LW("More than one prefixes present. Picking " "first one.")) for prefix in prefixes: cidr = netaddr.IPNetwork(prefix) pool_prefix_len = str(cidr.prefixlen) subnet_network = str(cidr.network) subnet_cidr = '/'.join([subnet_network, pool_prefix_len]) break else: raise exceptions.NoResourceException( "No subnetpools with id {0} is found.".format(pool_id)) # check if any subnet with matching cidr is present subnets = _get_subnets_by_attrs(cidr=subnet_cidr) if subnets: subnet = subnets[0] # allocating address for container port neutron_network_id = subnet['network_id'] try: port = { 'name': 'kuryr-unbound-port', 'admin_state_up': True, 'network_id': neutron_network_id, 'binding:host_id': utils.get_hostname(), } fixed_ips = port['fixed_ips'] = [] fixed_ip = {'subnet_id': subnet['id']} if req_address: fixed_ip['ip_address'] = req_address fixed_ips.append(fixed_ip) created_port_resp = app.neutron.create_port({'port': port}) created_port = created_port_resp['port'] allocated_address = created_port['fixed_ips'][0]['ip_address'] allocated_address = '/'.join( [allocated_address, str(cidr.prefixlen)]) except n_exceptions.NeutronClientException as ex: app.logger.error( _LE("Error happend during ip allocation on" "Neutron side: {0}").format(ex)) raise else: # Auxiliary address or gw_address is received at network creation time. # This address cannot be reserved with neutron at this time as subnet # is not created yet. In /NetworkDriver.CreateNetwork this address will # be reserved with neutron. if req_address: allocated_address = '/'.join([req_address, pool_prefix_len]) return flask.jsonify({'Address': allocated_address})
def ipam_request_pool(): """Creates a new Neutron subnetpool from the given request. This funciton takes the following JSON data and delegates the subnetpool creation to the Neutron client. :: { "AddressSpace": string "Pool": string "SubPool": string "Options": map[string]string "V6": bool } Then the following JSON response is returned. :: { "PoolID": string "Pool": string "Data": map[string]string } See the following link for more details about the spec: https://github.com/docker/libnetwork/blob/master/docs/ipam.md#requestpool # noqa """ json_data = flask.request.get_json(force=True) app.logger.debug("Received JSON data {0} for /IpamDriver.RequestPool" .format(json_data)) jsonschema.validate(json_data, schemata.REQUEST_POOL_SCHEMA) requested_pool = json_data['Pool'] requested_subpool = json_data['SubPool'] v6 = json_data['V6'] pool_id = '' subnet_cidr = '' if requested_pool: app.logger.info(_LI("Creating subnetpool with the given pool CIDR")) if requested_subpool: cidr = netaddr.IPNetwork(requested_subpool) else: cidr = netaddr.IPNetwork(requested_pool) subnet_cidr = _get_subnet_cidr_using_cidr(cidr) pool_name = utils.get_neutron_subnetpool_name(subnet_cidr) # Check if requested pool already exist pools = _get_subnetpools_by_attrs(name=pool_name) if pools: pool_id = pools[0]['id'] if not pools: new_subnetpool = { 'name': pool_name, 'default_prefixlen': cidr.prefixlen, 'prefixes': [subnet_cidr]} created_subnetpool_response = app.neutron.create_subnetpool( {'subnetpool': new_subnetpool}) pool = created_subnetpool_response['subnetpool'] pool_id = pool['id'] else: if v6: default_pool_list = SUBNET_POOLS_V6 else: default_pool_list = SUBNET_POOLS_V4 pool_name = default_pool_list[0] pools = _get_subnetpools_by_attrs(name=pool_name) if pools: pool = pools[0] pool_id = pool['id'] prefixes = pool['prefixes'] if len(prefixes) > 1: app.logger.warning(_LW("More than one prefixes present. " "Picking first one.")) cidr = netaddr.IPNetwork(prefixes[0]) subnet_cidr = _get_subnet_cidr_using_cidr(cidr) else: app.logger.error(_LE("Default neutron pools not found.")) req_pool_res = {'PoolID': pool_id, 'Pool': subnet_cidr} return flask.jsonify(req_pool_res)