def _get_combined_cert_for_server(self, server, port): # The ssl library requires a combined file with all trusted certs # so we make one containing the trusted CAs and the corresponding # host cert for this server combined_cert = None if self.ssl and not cfg.CONF.RESTPROXY.no_ssl_validation: base_ssl = cfg.CONF.RESTPROXY.ssl_cert_directory host_dir = os.path.join(base_ssl, 'host_certs') ca_dir = os.path.join(base_ssl, 'ca_certs') combined_dir = os.path.join(base_ssl, 'combined') combined_cert = os.path.join(combined_dir, '%s.pem' % server) if not os.path.exists(base_ssl): raise cfg.Error( _('ssl_cert_directory [%s] does not exist. ' 'Create it or disable ssl.') % base_ssl) for automake in [combined_dir, ca_dir, host_dir]: if not os.path.exists(automake): os.makedirs(automake) # get all CA certs certs = self._get_ca_cert_paths(ca_dir) # check for a host specific cert hcert, exists = self._get_host_cert_path(host_dir, server) if exists: certs.append(hcert) elif cfg.CONF.RESTPROXY.ssl_sticky: self._fetch_and_store_cert(server, port, hcert) certs.append(hcert) if not certs: raise cfg.Error( _('No certificates were found to verify ' 'controller %s') % (server)) self._combine_certs_to_file(certs, combined_cert) return combined_cert
def remove_router_interface(self, context, router_id, interface_info): # Validate args router = self._get_router(context, router_id) tenant_id = router['tenant_id'] # we will first get the interface identifier before deleting in the DB if not interface_info: msg = _("Either subnet_id or port_id must be specified") raise exceptions.BadRequest(resource='router', msg=msg) if 'port_id' in interface_info: port = self._get_port(context, interface_info['port_id']) interface_id = port['fixed_ips'][0]['subnet_id'] elif 'subnet_id' in interface_info: subnet = self._get_subnet(context, interface_info['subnet_id']) interface_id = subnet['id'] else: msg = _("Either subnet_id or port_id must be specified") raise exceptions.BadRequest(resource='router', msg=msg) with context.session.begin(subtransactions=True): # remove router in DB del_ret = super(L3RestProxy, self).remove_router_interface(context, router_id, interface_info) # create router on the network controller self.servers.rest_remove_router_interface(tenant_id, router_id, interface_id) return del_ret
def remove_router_interface(self, context, router_id, interface_info): # Validate args router = self._get_router(context, router_id) tenant_id = router['tenant_id'] # we will first get the interface identifier before deleting in the DB if not interface_info: msg = _("Either subnet_id or port_id must be specified") raise exceptions.BadRequest(resource='router', msg=msg) if 'port_id' in interface_info: port = self._get_port(context, interface_info['port_id']) interface_id = port['fixed_ips'][0]['subnet_id'] elif 'subnet_id' in interface_info: subnet = self._get_subnet(context, interface_info['subnet_id']) interface_id = subnet['id'] else: msg = _("Either subnet_id or port_id must be specified") raise exceptions.BadRequest(resource='router', msg=msg) with context.session.begin(subtransactions=True): # remove router in DB del_ret = super(L3RestProxy, self).remove_router_interface( context, router_id, interface_info) # create router on the network controller self.servers.rest_remove_router_interface(tenant_id, router_id, interface_id) return del_ret
def _get_combined_cert_for_server(self, server, port): # The ssl library requires a combined file with all trusted certs # so we make one containing the trusted CAs and the corresponding # host cert for this server combined_cert = None if self.ssl and not cfg.CONF.RESTPROXY.no_ssl_validation: base_ssl = cfg.CONF.RESTPROXY.ssl_cert_directory host_dir = os.path.join(base_ssl, 'host_certs') ca_dir = os.path.join(base_ssl, 'ca_certs') combined_dir = os.path.join(base_ssl, 'combined') combined_cert = os.path.join(combined_dir, '%s.pem' % server) if not os.path.exists(base_ssl): raise cfg.Error(_('ssl_cert_directory [%s] does not exist. ' 'Create it or disable ssl.') % base_ssl) for automake in [combined_dir, ca_dir, host_dir]: if not os.path.exists(automake): os.makedirs(automake) # get all CA certs certs = self._get_ca_cert_paths(ca_dir) # check for a host specific cert hcert, exists = self._get_host_cert_path(host_dir, server) if exists: certs.append(hcert) elif cfg.CONF.RESTPROXY.ssl_sticky: self._fetch_and_store_cert(server, port, hcert) certs.append(hcert) if not certs: raise cfg.Error(_('No certificates were found to verify ' 'controller %s') % (server)) self._combine_certs_to_file(certs, combined_cert) return combined_cert
def rest_get_switch(self, switch_id): resource = SWITCHES_PATH % switch_id errstr = _("Unable to retrieve switch: %s") resp = self.rest_action('GET', resource, errstr=errstr, ignore_codes=[404]) # return None if switch not found, else return switch info return None if resp[0] == 404 else resp[3]
def rest_update_router(self, tenant_id, router, router_id): self._check_and_raise_exception_unsupported_name( ObjTypeEnum.router, router) resource = ROUTERS_PATH % (tenant_id, router_id) data = {"router": router} errstr = _("Unable to update remote router: %s") self.rest_action('PUT', resource, data, errstr)
def _get_cached_vswitch_existence(self, host): """Returns cached existence. Expired and non-cached raise ValueError. """ entry = self.vswitch_host_cache.get(host) if not entry: raise ValueError(_('No cache entry for host %s') % host) diff = timeutils.delta_seconds(entry['timestamp'], datetime.datetime.now()) if diff > CACHE_VSWITCH_TIME: self.vswitch_host_cache.pop(host) raise ValueError(_('Expired cache entry for host %s') % host) if entry['exists']: return entry['type'] return None
def _fetch_and_store_cert(self, server, port, path): ''' Grabs a certificate from a server and writes it to a given path. ''' try: cert = ssl.get_server_certificate((server, port), ssl_version=ssl.PROTOCOL_TLSv1) except Exception as e: raise cfg.Error( _('Could not retrieve initial ' 'certificate from controller %(server)s. ' 'Error details: %(error)s') % { 'server': server, 'error': e }) LOG.warning( _LW("Storing to certificate for host %(server)s " "at %(path)s"), { 'server': server, 'path': path }) self._file_put_contents(path, cert) return cert
def _send_all_data(self, send_ports=True, send_floating_ips=True, send_routers=True, send_sgs=True, timeout=None, triggered_by_tenant=None): """Pushes all data to network ctrl (networks/ports, ports/attachments). This gives the controller an option to re-sync it's persistent store with neutron's current view of that data. """ data = self._get_all_data(send_ports, send_floating_ips, send_routers, send_sgs) # Lost keystone connection if data is None # Log an error and continue if data is None: return None data['triggered_by_tenant'] = triggered_by_tenant errstr = _("Unable to update remote topology: %s") return self.servers.rest_action('POST', servermanager.TOPOLOGY_PATH, data, errstr, timeout=timeout)
def rest_create_network(self, tenant_id, network): self._check_and_raise_exception_unsupported_name( ObjTypeEnum.network, network) resource = NET_RESOURCE_PATH % tenant_id data = {"network": network} errstr = _("Unable to create remote network: %s") self.rest_action('POST', resource, data, errstr)
class TenantIDNotFound(exceptions.NeutronException): message = _("Tenant: %(tenant)s is not known by keystone.") status = None def __init__(self, **kwargs): self.tenant = kwargs.get('tenant') super(TenantIDNotFound, self).__init__(**kwargs)
def rest_update_network(self, tenant_id, net_id, network): self._check_and_raise_exception_unsupported_name( ObjTypeEnum.network, network) resource = NETWORKS_PATH % (tenant_id, net_id) data = {"network": network} errstr = _("Unable to update remote network: %s") self.rest_action('PUT', resource, data, errstr)
def rest_create_securitygroup(self, sg): self._check_and_raise_exception_unsupported_name( ObjTypeEnum.security_group, sg) resource = SECURITY_GROUP_RESOURCE_PATH data = {"security-group": sg} errstr = _("Unable to create security group: %s") self.rest_action('POST', resource, data, errstr)
def rest_create_router(self, tenant_id, router): self._check_and_raise_exception_unsupported_name( ObjTypeEnum.router, router) resource = ROUTER_RESOURCE_PATH % tenant_id data = {"router": router} errstr = _("Unable to create remote router: %s") self.rest_action('POST', resource, data, errstr)
def rest_get_port(self, tenant_id, net_id, port_id): resource = ATTACHMENT_PATH % (tenant_id, net_id, port_id) errstr = _("Unable to retrieve port: %s") resp = self.rest_action('GET', resource, errstr=errstr, ignore_codes=[404]) return None if resp[0] == 404 else resp[3]
def _rest_create_tenant(self, tenant_id): tenant_name = self.keystone_tenants.get(tenant_id) if not tenant_name: raise TenantIDNotFound(tenant=tenant_id) resource = TENANT_RESOURCE_PATH data = {"tenant_id": tenant_id, 'tenant_name': tenant_name} errstr = _("Unable to create tenant: %s") self.rest_action('POST', resource, data, errstr)
def connect(self): contents = get_cert_contents(self.combined_cert) expected = self.expected_cert % self.host if expected not in contents: raise Exception(_('No host cert for %(server)s in cert %(cert)s'), { 'server': self.host, 'cert': contents })
def _validate_nexthops(nexthops): seen = [] for ip in nexthops: msg = validators.validate_ip_address(ip) if ip in seen: msg = _("Duplicate nexthop in rule '%s'") % ip seen.append(ip) if msg: return msg
class RemoteRestError(exceptions.NeutronException): message = _("Error in REST call to remote network " "controller: %(reason)s") status = None def __init__(self, **kwargs): self.status = kwargs.pop('status', None) self.reason = kwargs.get('reason') super(RemoteRestError, self).__init__(**kwargs)
def _validate_nexthops(nexthops): seen = [] for ip in nexthops: msg = attr._validate_ip_address(ip) if ip in seen: msg = _("Duplicate nexthop in rule '%s'") % ip seen.append(ip) if msg: return msg
def rest_get_testpath(self, src, dst): resource = TESTPATH_PATH % {'src-tenant': src['tenant'], 'src-segment': src['segment'], 'src-ip': src['ip'], 'dst-ip': dst['ip']} errstr = _("Unable to retrieve results for testpath ID: %s") resp = self.rest_action('GET', resource, errstr=errstr, ignore_codes=[404]) # return None if testpath not found, else return testpath info return None if (resp[0] not in range(200, 300)) else resp[3]
def request(self, action, uri, body, headers): # Only handle network update requests if 'network' in uri and 'tenant' in uri and 'ports' not in uri: req = jsonutils.loads(body) if 'network' not in req or 'floatingips' not in req['network']: msg = _("No floating IPs in request" "uri=%(uri)s, body=%(body)s") % {'uri': uri, 'body': body} raise Exception(msg) distinct_tenants = [] for flip in req['network']['floatingips']: if flip['tenant_id'] not in distinct_tenants: distinct_tenants.append(flip['tenant_id']) if len(distinct_tenants) < 2: msg = _("Expected floating IPs from multiple tenants." "uri=%(uri)s, body=%(body)s") % {'uri': uri, 'body': body} raise Exception(msg) super(VerifyMultiTenantFloatingIP, self).request(action, uri, body, headers)
class UnsupportedNameException(exceptions.NeutronException): """ Exception class to be raised when encountering object names with unsupported names. Namely those that do not conform to the regular expression BCF_IDENTIFIER_RE :keyword obj_type :keyword obj_id :keyword obj_name """ message = _("Object of type %(obj_type)s and id %(obj_id)s has unsupported" " character in name \"%(obj_name)s\"") status = None
def rest_create_port(self, tenant_id, net_id, port): resource = ATTACHMENT_PATH % (tenant_id, net_id, port["id"]) data = {"port": port} device_id = port.get("device_id") if not port["mac_address"] or not device_id: # controller only cares about ports attached to devices LOG.warning( _LW("No device MAC attached to port %s. " "Skipping notification to controller."), port["id"]) return data["attachment"] = {"id": device_id, "mac": port["mac_address"]} errstr = _("Unable to create remote port: %s") self.rest_action('PUT', resource, data, errstr)
def rest_create_port(self, tenant_id, net_id, port): resource = ATTACHMENT_PATH % (tenant_id, net_id, port["id"]) data = {"port": port} device_id = port.get("device_id") if not port["mac_address"] or not device_id: # controller only cares about ports attached to devices LOG.warning(_LW("No device MAC attached to port %s. " "Skipping notification to controller."), port["id"]) return data["attachment"] = {"id": device_id, "mac": port["mac_address"]} errstr = _("Unable to create remote port: %s") self.rest_action('PUT', resource, data, errstr)
def _validate_uniquerules(rules): pairs = [] for r in rules: if ('source' not in r or 'destination' not in r or 'action' not in r or 'priority' not in r): continue pairs.append((r['source'], r['destination'], r['action'], r['priority'])) if len(set(pairs)) != len(pairs): error = _("Duplicate router rules (src,dst,action,priority) " "found '%s'") % pairs LOG.debug(error) raise nexception.InvalidInput(error_message=error)
def rest_get_testpath(self, src, dst): resource = TESTPATH_PATH % { 'src-tenant': src['tenant'], 'src-segment': src['segment'], 'src-ip': src['ip'], 'dst-ip': dst['ip'] } errstr = _("Unable to retrieve results for testpath ID: %s") resp = self.rest_action('GET', resource, errstr=errstr, ignore_codes=[404]) # return None if testpath not found, else return testpath info return None if (resp[0] not in range(200, 300)) else resp[3]
def _rest_create_tenant(self, tenant_id): tenant_name = self.keystone_tenants.get(tenant_id) if not tenant_name: raise TenantIDNotFound(tenant=tenant_id) if not is_valid_bcf_name(tenant_name): raise UnsupportedNameException(obj_type=ObjTypeEnum.tenant, obj_id=tenant_id, obj_name=tenant_name) resource = TENANT_RESOURCE_PATH data = {"tenant_id": tenant_id, 'tenant_name': tenant_name} errstr = _("Unable to create tenant: %s") self.rest_action('POST', resource, data, errstr)
def request(self, action, uri, body, headers): # Only handle network update requests if 'network' in uri and 'tenant' in uri and 'ports' not in uri: req = jsonutils.loads(body) if 'network' not in req or 'floatingips' not in req['network']: msg = _("No floating IPs in request" "uri=%(uri)s, body=%(body)s") % { 'uri': uri, 'body': body } raise Exception(msg) distinct_tenants = [] for flip in req['network']['floatingips']: if flip['tenant_id'] not in distinct_tenants: distinct_tenants.append(flip['tenant_id']) if len(distinct_tenants) < 2: msg = _("Expected floating IPs from multiple tenants." "uri=%(uri)s, body=%(body)s") % { 'uri': uri, 'body': body } raise Exception(msg) super(VerifyMultiTenantFloatingIP, self).request(action, uri, body, headers)
class UnsupportedTenantNameInObjectException(exceptions.NeutronException): """ Exception class to be raised when objects have tenant names with unsupported characters. Namely those that do not conform to the regular expression BCF_IDENTIFIER_RE :keyword obj_type :keyword obj_id :keyword obj_name :keyword tenant_name """ message = _("Object of type %(obj_type)s, id %(obj_id)s and name " "%(obj_name)s has unsupported character in its tenant name " "\"%(tenant_name)s\"") status = None
def _send_all_data(self, send_ports=True, send_floating_ips=True, send_routers=True, send_sgs=True, timeout=None, triggered_by_tenant=None): """Pushes all data to network ctrl (networks/ports, ports/attachments). This gives the controller an option to re-sync it's persistent store with neutron's current view of that data. """ data = self._get_all_data( send_ports, send_floating_ips, send_routers, send_sgs) # Lost keystone connection if data is None # Log an error and continue if data is None: return None data['triggered_by_tenant'] = triggered_by_tenant errstr = _("Unable to update remote topology: %s") return self.servers.rest_action('POST', servermanager.TOPOLOGY_PATH, data, errstr, timeout=timeout)
def _fetch_and_store_cert(self, server, port, path): ''' Grabs a certificate from a server and writes it to a given path. ''' try: cert = ssl.get_server_certificate((server, port), ssl_version=ssl.PROTOCOL_TLSv1) except Exception as e: raise cfg.Error(_('Could not retrieve initial ' 'certificate from controller %(server)s. ' 'Error details: %(error)s') % {'server': server, 'error': e}) LOG.warning(_LW("Storing to certificate for host %(server)s " "at %(path)s"), {'server': server, 'path': path}) self._file_put_contents(path, cert) return cert
def convert_to_valid_router_rules(data): """ Validates and converts router rules to the appropriate data structure Example argument = [{'source': 'any', 'destination': 'any', 'action':'deny'}, {'source': '1.1.1.1/32', 'destination': 'external', 'action':'permit', 'nexthops': ['1.1.1.254', '1.1.1.253']} ] """ V4ANY = '0.0.0.0/0' CIDRALL = ['any', 'external'] if not isinstance(data, list): emsg = _("Invalid data format for router rule: '%s'") % data LOG.debug(emsg) raise nexception.InvalidInput(error_message=emsg) _validate_uniquerules(data) rules = [] expected_keys = ['source', 'destination', 'action', 'priority'] for rule in data: rule['nexthops'] = rule.get('nexthops', []) if not isinstance(rule['nexthops'], list): rule['nexthops'] = rule['nexthops'].split('+') src = V4ANY if rule['source'] in CIDRALL else rule['source'] dst = V4ANY if rule['destination'] in CIDRALL else rule['destination'] errors = [ validators._verify_dict_keys(expected_keys, rule, False), validators.validate_subnet(dst), validators.validate_subnet(src), _validate_nexthops(rule['nexthops']), _validate_action(rule['action']) ] errors = [m for m in errors if m] if errors: LOG.debug(errors) raise nexception.InvalidInput(error_message=errors) rules.append(rule) return rules
def convert_to_valid_router_rules(data): """ Validates and converts router rules to the appropriate data structure Example argument = [{'source': 'any', 'destination': 'any', 'action':'deny'}, {'source': '1.1.1.1/32', 'destination': 'external', 'action':'permit', 'nexthops': ['1.1.1.254', '1.1.1.253']} ] """ V4ANY = '0.0.0.0/0' CIDRALL = ['any', 'external'] if not isinstance(data, list): emsg = _("Invalid data format for router rule: '%s'") % data LOG.debug(emsg) raise nexception.InvalidInput(error_message=emsg) _validate_uniquerules(data) rules = [] expected_keys = ['source', 'destination', 'action', 'priority'] for rule in data: rule['nexthops'] = rule.get('nexthops', []) if not isinstance(rule['nexthops'], list): rule['nexthops'] = rule['nexthops'].split('+') src = V4ANY if rule['source'] in CIDRALL else rule['source'] dst = V4ANY if rule['destination'] in CIDRALL else rule['destination'] errors = [attr._verify_dict_keys(expected_keys, rule, False), attr._validate_subnet(dst), attr._validate_subnet(src), _validate_nexthops(rule['nexthops']), _validate_action(rule['action'])] errors = [m for m in errors if m] if errors: LOG.debug(errors) raise nexception.InvalidInput(error_message=errors) rules.append(rule) return rules
def rest_delete_floatingip(self, tenant_id, oldid): resource = FLOATINGIPS_PATH % (tenant_id, oldid) errstr = _("Unable to delete floating IP: %s") self.rest_action('DELETE', resource, errstr=errstr)
def rest_create_network(self, tenant_id, network): resource = NET_RESOURCE_PATH % tenant_id data = {"network": network} errstr = _("Unable to create remote network: %s") self.rest_action('POST', resource, data, errstr)
def rest_update_network(self, tenant_id, net_id, network): resource = NETWORKS_PATH % (tenant_id, net_id) data = {"network": network} errstr = _("Unable to update remote network: %s") self.rest_action('PUT', resource, data, errstr)
def rest_create_floatingip(self, tenant_id, floatingip): resource = FLOATINGIPS_PATH % (tenant_id, floatingip['id']) errstr = _("Unable to create floating IP: %s") self.rest_action('PUT', resource, floatingip, errstr=errstr)
def rest_delete_port(self, tenant_id, network_id, port_id): resource = ATTACHMENT_PATH % (tenant_id, network_id, port_id) errstr = _("Unable to delete remote port: %s") self.rest_action('DELETE', resource, errstr=errstr)
from neutron import manager from neutron.plugins.common import constants as pconst from bsnstacklib.plugins.bigswitch import config as pl_config from bsnstacklib.plugins.bigswitch.db import porttracker_db from bsnstacklib.plugins.bigswitch import extensions from bsnstacklib.plugins.bigswitch.i18n import _ from bsnstacklib.plugins.bigswitch.i18n import _LE from bsnstacklib.plugins.bigswitch.i18n import _LI from bsnstacklib.plugins.bigswitch.i18n import _LW from bsnstacklib.plugins.bigswitch import servermanager from bsnstacklib.plugins.bigswitch import version LOG = logging.getLogger(__name__) SYNTAX_ERROR_MESSAGE = _('Syntax error in server config file, aborting plugin') METADATA_SERVER_IP = '169.254.169.254' class AgentNotifierApi(sg_rpc.SecurityGroupAgentRpcApiMixin): def __init__(self, topic): self.topic = topic target = oslo_messaging.Target(topic=topic, version='1.0') self.client = n_rpc.get_client(target) def port_update(self, context, port): topic_port_update = topics.get_topic_name(self.client.target.topic, topics.PORT, topics.UPDATE) cctxt = self.client.prepare(fanout=True, topic=topic_port_update) cctxt.cast(context, 'port_update', port=port)
def connect(self): contents = get_cert_contents(self.combined_cert) expected = self.expected_cert % self.host if expected not in contents: raise Exception(_('No host cert for %(server)s in cert %(cert)s'), {'server': self.host, 'cert': contents})
def rest_remove_router_interface(self, tenant_id, router_id, interface_id): resource = ROUTER_INTF_PATH % (tenant_id, router_id, interface_id) errstr = _("Unable to delete remote intf: %s") self.rest_action('DELETE', resource, errstr=errstr)
class NetworkTemplateAssignmentExists(exceptions.NeutronException): message = \ _("Network Template Assignment for tenant ID %(tenant_id)s exists")
def rest_delete_router(self, tenant_id, router_id): resource = ROUTERS_PATH % (tenant_id, router_id) errstr = _("Unable to delete remote router: %s") self.rest_action('DELETE', resource, errstr=errstr)
def rest_add_router_interface(self, tenant_id, router_id, intf_details): resource = ROUTER_INTF_OP_PATH % (tenant_id, router_id) data = {"interface": intf_details} errstr = _("Unable to add router interface: %s") self.rest_action('POST', resource, data, errstr)
def _validate_action(action): if action not in ['permit', 'deny']: return _("Action must be either permit or deny." " '%s' was provided") % action
def get_plugin_description(): return _("L3 Router Service Plugin for Big Switch fabric")
def _validate_priority(priority): if int(priority) < 1: msg = _("User must provide valid priority between 1 and 3000. " "%s was provided.") % priority return msg
def rest_delete_securitygroup(self, sg_id): resource = SECURITY_GROUP_PATH % sg_id errstr = _("Unable to delete security group: %s") self.rest_action('DELETE', resource, errstr=errstr)
def rest_create_securitygroup(self, sg): resource = SECURITY_GROUP_RESOURCE_PATH data = {"security-group": sg} errstr = _("Unable to create security group: %s") self.rest_action('POST', resource, data, errstr)
class NetworkTemplateNotFound(exceptions.NotFound): message = _("Network Template %(id)s could not be found")
def rest_delete_network(self, tenant_id, net_id): resource = NETWORKS_PATH % (tenant_id, net_id) errstr = _("Unable to delete remote network: %s") self.rest_action('DELETE', resource, errstr=errstr)
def rest_update_floatingip(self, tenant_id, floatingip, oldid): resource = FLOATINGIPS_PATH % (tenant_id, oldid) errstr = _("Unable to update floating IP: %s") self.rest_action('PUT', resource, floatingip, errstr=errstr)