components/openstack/neutron/files/evs/plugin.py
changeset 3998 5bd484384122
parent 3524 ad6a9e0880b9
child 4507 76c2e35382f6
equal deleted inserted replaced
3997:0ca3f3d6c919 3998:5bd484384122
     1 # vim: tabstop=4 shiftwidth=4 softtabstop=4
     1 # vim: tabstop=4 shiftwidth=4 softtabstop=4
     2 
     2 
     3 # Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
     3 # Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
     4 #
     4 #
     5 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
     5 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
     6 #    not use this file except in compliance with the License. You may obtain
     6 #    not use this file except in compliance with the License. You may obtain
     7 #    a copy of the License at
     7 #    a copy of the License at
     8 #
     8 #
    14 #    License for the specific language governing permissions and limitations
    14 #    License for the specific language governing permissions and limitations
    15 #    under the License.
    15 #    under the License.
    16 #
    16 #
    17 # @author: Girish Moodalbail, Oracle, Inc.
    17 # @author: Girish Moodalbail, Oracle, Inc.
    18 
    18 
    19 import netaddr
       
    20 import rad.client as radcli
    19 import rad.client as radcli
    21 import rad.connect as radcon
    20 import rad.connect as radcon
    22 import rad.bindings.com.oracle.solaris.rad.evscntl_1 as evsbind
    21 import rad.bindings.com.oracle.solaris.rad.evscntl_1 as evsbind
    23 
    22 
    24 from oslo.config import cfg
    23 from oslo.config import cfg
    25 
    24 
    26 from neutron.api.rpc.agentnotifiers import dhcp_rpc_agent_api
    25 from neutron.api.rpc.agentnotifiers import dhcp_rpc_agent_api
    27 from neutron.api.rpc.agentnotifiers import l3_rpc_agent_api
    26 from neutron.api.rpc.agentnotifiers import l3_rpc_agent_api
       
    27 from neutron.api.rpc.handlers import dhcp_rpc
       
    28 from neutron.api.rpc.handlers import l3_rpc
    28 from neutron.api.v2 import attributes
    29 from neutron.api.v2 import attributes
    29 from neutron.common import constants as l3_constants
    30 from neutron.common import constants
    30 from neutron.common import exceptions
    31 from neutron.common import exceptions
    31 from neutron.common import rpc as q_rpc
    32 from neutron.common import rpc as n_rpc
    32 from neutron.common import topics
    33 from neutron.common import topics
       
    34 from neutron.db import agents_db
       
    35 from neutron.db import agentschedulers_db
       
    36 from neutron.db import api as db
    33 from neutron.db import db_base_plugin_v2
    37 from neutron.db import db_base_plugin_v2
    34 from neutron.db import dhcp_rpc_base
       
    35 from neutron.db import external_net_db
    38 from neutron.db import external_net_db
    36 from neutron.db import l3_rpc_base
    39 from neutron.db import l3_gwmode_db
       
    40 from neutron.db import model_base
       
    41 from neutron.db import quota_db
    37 from neutron.extensions import external_net
    42 from neutron.extensions import external_net
    38 from neutron.extensions import providernet
    43 from neutron.extensions import providernet
       
    44 from neutron.openstack.common import importutils
    39 from neutron.openstack.common import lockutils
    45 from neutron.openstack.common import lockutils
    40 from neutron.openstack.common import log as logging
    46 from neutron.openstack.common import log as logging
    41 from neutron.openstack.common import rpc
       
    42 from neutron.plugins.common import constants as svc_constants
    47 from neutron.plugins.common import constants as svc_constants
    43 from neutron.plugins.evs.db import api as evs_db
       
    44 from neutron.plugins.evs.db import l3nat as evs_l3nat
       
    45 from neutron.plugins.evs.db import quotas_db
       
    46 
       
    47 
    48 
    48 LOG = logging.getLogger(__name__)
    49 LOG = logging.getLogger(__name__)
    49 
    50 
    50 evs_controller_opts = [
    51 evs_controller_opts = [
    51     cfg.StrOpt('evs_controller', default='ssh://evsuser@localhost',
    52     cfg.StrOpt('evs_controller', default='ssh://evsuser@localhost',
    52                help=_("An URI that specifies an EVS controller"))
    53                help=_("An URI that specifies an EVS controller"))
    53 ]
    54 ]
    54 
    55 
    55 evs_database_opts = [
       
    56     cfg.StrOpt('sql_connection',
       
    57                default='sqlite:////var/lib/neutron/neutron.sqlite',
       
    58                help=_("An URI that specifies SQL connectionr")),
       
    59 ]
       
    60 
       
    61 cfg.CONF.register_opts(evs_controller_opts, "EVS")
    56 cfg.CONF.register_opts(evs_controller_opts, "EVS")
    62 cfg.CONF.register_opts(evs_database_opts, "DATABASE")
       
    63 
       
    64 # Maps OpenStack network resource attributes to EVS properties
       
    65 NETWORK_EVS_ATTRIBUTE_MAP = {
       
    66     'tenant_id': 'tenant',
       
    67     'network_id': 'evs',
       
    68     'id': 'evs',
       
    69     'name': 'evs',
       
    70     external_net.EXTERNAL: 'OpenStack:' + external_net.EXTERNAL,
       
    71 }
       
    72 
       
    73 # Maps OpenStack subnet resource attributes to EVS' IPnet properties
       
    74 SUBNET_IPNET_ATTRIBUTE_MAP = {
       
    75     'tenant_id': 'tenant',
       
    76     'network_id': 'evs',
       
    77     'id': 'ipnet',
       
    78     'name': 'ipnet',
       
    79     'enable_dhcp': 'OpenStack:enable_dhcp',
       
    80     'dns_nameservers': 'OpenStack:dns_nameservers',
       
    81     'host_routes': 'OpenStack:host_routes',
       
    82 }
       
    83 
       
    84 # Maps OpenStack port resource attributes to EVS' VPort properties
       
    85 PORT_VPORT_ATTRIBUTE_MAP = {
       
    86     'tenant_id': 'tenant',
       
    87     'network_id': 'evs',
       
    88     'id': 'vport',
       
    89     'name': 'vport',
       
    90     'device_id': 'OpenStack:device_id',
       
    91     'device_owner': 'OpenStack:device_owner',
       
    92 }
       
    93 
    57 
    94 
    58 
    95 class EVSControllerError(exceptions.NeutronException):
    59 class EVSControllerError(exceptions.NeutronException):
    96     message = _("EVS controller: %(errmsg)s")
    60     message = _("EVS controller: %(errmsg)s")
    97 
    61 
   104 
    68 
   105     def __init__(self, evs_errmsg):
    69     def __init__(self, evs_errmsg):
   106         super(EVSOpNotSupported, self).__init__(opname=evs_errmsg)
    70         super(EVSOpNotSupported, self).__init__(opname=evs_errmsg)
   107 
    71 
   108 
    72 
   109 class EVSRpcCallbacks(dhcp_rpc_base.DhcpRpcCallbackMixin,
    73 class EVSNotFound(exceptions.NeutronException):
   110                       l3_rpc_base.L3RpcCallbackMixin):
    74     message = _("Network %(net_id)s could not be found in EVS")
   111     RPC_API_VERSION = '1.1'
       
   112 
       
   113     def create_rpc_dispatcher(self):
       
   114         '''Get the rpc dispatcher for this manager.
       
   115 
       
   116         If a manager would like to set an rpc API version, or support more than
       
   117         one class as the target of rpc messages, override this method.
       
   118         '''
       
   119         return q_rpc.PluginRpcDispatcher([self])
       
   120 
       
   121     def report_state(self, context, **kwargs):
       
   122         # TODO(gmoodalb): This method is currently no-op and is included
       
   123         # here to avoid Python traceback thrown every time Neutron L3/DHCP
       
   124         # agent is restarted. When we support Network Agents Information,
       
   125         # we will implement this function
       
   126         pass
       
   127 
    75 
   128 
    76 
   129 class EVSNeutronPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
    77 class EVSNeutronPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
       
    78                          agentschedulers_db.DhcpAgentSchedulerDbMixin,
   130                          external_net_db.External_net_db_mixin,
    79                          external_net_db.External_net_db_mixin,
   131                          evs_l3nat.EVS_L3_NAT_db_mixin):
    80                          l3_gwmode_db.L3_NAT_db_mixin):
   132     """Implements v2 Neutron Plug-in API specification.
    81     """Implements v2 Neutron Plug-in API specification.
   133 
    82 
   134     All the neutron API calls to create/delete/retrieve Network/Subnet/Port
    83     All the neutron API calls to create/delete/retrieve Network/Subnet/Port
   135     are forwarded to EVS controller through Solaris RAD. The RAD connection
    84     are forwarded to EVS controller through Solaris RAD. The RAD connection
   136     to EVS Controller is over SSH. In order that this plugin can communicate
    85     to EVS Controller is over SSH. In order that this plugin can communicate
   188     | -- security_groups  | -- Not Supported |                              |
   137     | -- security_groups  | -- Not Supported |                              |
   189     | -- admin_state_up   | Always UP        |                              |
   138     | -- admin_state_up   | Always UP        |                              |
   190     |---------------------+------------------+------------------------------|
   139     |---------------------+------------------+------------------------------|
   191     """
   140     """
   192 
   141 
   193     # These attribute specifies whether the plugin supports or not
       
   194     # bulk/pagination/sorting operations.
       
   195     __native_bulk_support = False
       
   196     __native_pagination_support = False
       
   197     __native_sorting_support = False
       
   198 
       
   199     _supported_extension_aliases = ["provider", "external-net", "router",
   142     _supported_extension_aliases = ["provider", "external-net", "router",
   200                                     "quotas"]
   143                                     "ext-gw-mode", "quotas", "agent",
       
   144                                     "dhcp_agent_scheduler"]
   201 
   145 
   202     def __init__(self):
   146     def __init__(self):
   203         # Since EVS Framework does not support router and floatingip
   147         engine = db.get_engine()
   204         # resources, the plugin itself will maintain a DB for these
   148         model_base.BASEV2.metadata.create_all(engine)
   205         # two resources
   149 
   206         evs_db.configure_db()
   150         self.network_scheduler = importutils.import_object(
   207 
   151             cfg.CONF.network_scheduler_driver
   208         # Since there is no connect_uri() yet, we need to do this ourselves
   152         )
   209         # parse ssh://user@hostname
   153 
       
   154         self._setup_rpc()
       
   155         self._rad_connection = None
       
   156 
       
   157     @property
       
   158     def rad_connection(self):
       
   159         # Since there is no connect_uri() yet, we need to do
       
   160         # parsing of ssh://user@hostname ourselves
   210         suh = cfg.CONF.EVS.evs_controller.split('://')
   161         suh = cfg.CONF.EVS.evs_controller.split('://')
   211         if len(suh) != 2 or suh[0] != 'ssh' or not suh[1].strip():
   162         if len(suh) != 2 or suh[0] != 'ssh' or not suh[1].strip():
   212             raise SystemExit(_("Specified evs_controller is invalid"))
   163             raise SystemExit(_("Specified evs_controller is invalid"))
   213         uh = suh[1].split('@')
   164         uh = suh[1].split('@')
   214         if len(uh) != 2 or not uh[0].strip() or not uh[1].strip():
   165         if len(uh) != 2 or not uh[0].strip() or not uh[1].strip():
   215             raise SystemExit(_("'user' and 'hostname' need to be specified "
   166             raise SystemExit(_("'user' and 'hostname' need to be specified "
   216                                "for evs_controller"))
   167                                "for evs_controller"))
   217 
   168 
   218         # TODO(gmoodalb): - try few times before you give up
   169         if (self._rad_connection is not None and
   219         self._rc = radcon.connect_ssh(uh[1], user=uh[0])
   170                 self._rad_connection._closed is None):
   220         self._evsc = self._rc.get_object(evsbind.EVSController())
   171             return self._rad_connection
   221         self._setup_rpc()
   172 
       
   173         LOG.debug(_("Connecting to EVS Controller at %s as %s") %
       
   174                   (uh[1], uh[0]))
       
   175         self._rad_connection = radcon.connect_ssh(uh[1], user=uh[0])
       
   176         return self._rad_connection
   222 
   177 
   223     def _setup_rpc(self):
   178     def _setup_rpc(self):
   224         # RPC support
   179         # RPC support
   225         self.service_topics = {svc_constants.CORE: topics.PLUGIN,
   180         self.service_topics = {svc_constants.CORE: topics.PLUGIN,
   226                                svc_constants.L3_ROUTER_NAT: topics.L3PLUGIN}
   181                                svc_constants.L3_ROUTER_NAT: topics.L3PLUGIN}
   227         self.conn = rpc.create_connection(new=True)
   182         self.conn = n_rpc.create_connection(new=True)
   228         self.callbacks = EVSRpcCallbacks()
   183         self.endpoints = [dhcp_rpc.DhcpRpcCallback(),
   229         self.dispatcher = self.callbacks.create_rpc_dispatcher()
   184                           l3_rpc.L3RpcCallback(),
       
   185                           agents_db.AgentExtRpcCallback()]
   230         for svc_topic in self.service_topics.values():
   186         for svc_topic in self.service_topics.values():
   231             self.conn.create_consumer(svc_topic, self.dispatcher, fanout=False)
   187             self.conn.create_consumer(svc_topic, self.endpoints, fanout=False)
   232         # Consume from all consumers in a thread
   188         # Consume from all consumers in a thread
   233         self.conn.consume_in_thread()
   189         self.conn.consume_in_threads()
   234         self.dhcp_agent_notifier = dhcp_rpc_agent_api.DhcpAgentNotifyAPI()
   190         self.dhcp_agent_notifier = dhcp_rpc_agent_api.DhcpAgentNotifyAPI()
       
   191         self.l3_agent_notifier = l3_rpc_agent_api.L3AgentNotifyAPI()
       
   192 
       
   193         # needed by AgentSchedulerDbMixin()
       
   194         self.agent_notifiers[constants.AGENT_TYPE_DHCP] = \
       
   195             self.dhcp_agent_notifier
       
   196         self.agent_notifiers[constants.AGENT_TYPE_L3] = \
       
   197             self.l3_agent_notifier
   235 
   198 
   236     @property
   199     @property
   237     def supported_extension_aliases(self):
   200     def supported_extension_aliases(self):
   238         return self._supported_extension_aliases
   201         return self._supported_extension_aliases
   239 
   202 
   240     def _convert_evs_to_network(self, evs):
       
   241         """Converts an EVS structure into Neutron Network structure."""
       
   242 
       
   243         networkdict = dict()
       
   244         networkdict['name'] = evs.name
       
   245         networkdict['id'] = evs.uuid
       
   246         networkdict['subnets'] = ([ipnet.uuid for ipnet in evs.ipnets]
       
   247                                   if evs.ipnets else [])
       
   248         networkdict['tenant_id'] = evs.tenantname
       
   249         networkdict[external_net.EXTERNAL] = False
       
   250         for prop in evs.props:
       
   251             if prop.name == 'l2-type':
       
   252                 networkdict[providernet.NETWORK_TYPE] = prop.value
       
   253             elif prop.name == 'vlanid' or prop.name == 'vni':
       
   254                 networkdict[providernet.SEGMENTATION_ID] = int(prop.value)
       
   255             elif prop.name == NETWORK_EVS_ATTRIBUTE_MAP[external_net.EXTERNAL]:
       
   256                 networkdict[external_net.EXTERNAL] = \
       
   257                     (True if prop.value == 'True' else False)
       
   258         # fixed values as EVS framework doesn't support this
       
   259         networkdict['admin_state_up'] = True
       
   260         networkdict['status'] = 'ACTIVE'
       
   261         networkdict['shared'] = False
       
   262 
       
   263         return networkdict
       
   264 
       
   265     def _convert_ipnet_to_subnet(self, ipnet):
       
   266         """Converts an EVS IPnet structure into Neutron Subnet structure."""
       
   267 
       
   268         subnetdict = dict()
       
   269         subnetdict['name'] = ipnet.name
       
   270         subnetdict['network_id'] = ipnet.evsuuid
       
   271         subnetdict['id'] = ipnet.uuid
       
   272         subnetdict['tenant_id'] = ipnet.tenantname
       
   273         subnetdict['ip_version'] = \
       
   274             (4 if ipnet.ipvers == evsbind.IPVersion.IPV4 else 6)
       
   275         # assign default values to some subnet attributes
       
   276         subnetdict['dns_nameservers'] = []
       
   277         subnetdict['host_routes'] = []
       
   278         subnetdict['enable_dhcp'] = False
       
   279         for prop in ipnet.props:
       
   280             if prop.name == 'defrouter':
       
   281                 subnetdict['gateway_ip'] = prop.value
       
   282             elif prop.name == 'subnet':
       
   283                 subnetdict['cidr'] = prop.value
       
   284             elif prop.name == SUBNET_IPNET_ATTRIBUTE_MAP['enable_dhcp']:
       
   285                 subnetdict['enable_dhcp'] = \
       
   286                     (True if prop.value == 'True' else False)
       
   287             elif prop.name == SUBNET_IPNET_ATTRIBUTE_MAP['dns_nameservers']:
       
   288                 subnetdict['dns_nameservers'] = prop.value.split(',')
       
   289             elif prop.name == SUBNET_IPNET_ATTRIBUTE_MAP['host_routes']:
       
   290                 hrlist = []
       
   291                 vlist = prop.value.split(',')
       
   292                 for i in range(0, len(vlist), 2):
       
   293                     hrlist.append({vlist[i]: vlist[i + 1]})
       
   294                 subnetdict['host_routes'] = hrlist
       
   295             elif prop.name == 'pool':
       
   296                 poollist = []
       
   297                 for pool in prop.value.split(','):
       
   298                     if '-' not in pool:
       
   299                         start = end = pool
       
   300                     else:
       
   301                         start, end = pool.split('-')
       
   302                     poollist.append(dict(start=start, end=end))
       
   303                 subnetdict['allocation_pools'] = poollist
       
   304         subnetdict['shared'] = False
       
   305 
       
   306         return subnetdict
       
   307 
       
   308     def _convert_vport_to_port(self, context, vport):
       
   309         """Converts an EVS VPort structure into Neutron port structure."""
       
   310 
       
   311         portdict = dict()
       
   312         portdict['admin_state_up'] = True
       
   313         portdict['id'] = vport.uuid
       
   314         portdict['name'] = vport.name
       
   315         portdict['network_id'] = vport.evsuuid
       
   316         # TODO(gmoodalb): set to host/zonename/vnicname?
       
   317         portdict['device_id'] = ''
       
   318         portdict['device_owner'] = ''
       
   319         for prop in vport.props:
       
   320             if (prop.name == 'macaddr'):
       
   321                 portdict['mac_address'] = prop.value
       
   322             elif (prop.name == 'ipaddr'):
       
   323                 evs = self.get_network(context, vport.evsuuid)
       
   324                 portdict['fixed_ips'] = \
       
   325                     [{
       
   326                         'ip_address': prop.value.split('/')[0],
       
   327                         'subnet_id': evs['subnets'][0],
       
   328                     }]
       
   329             elif (prop.name == 'OpenStack:device_id'):
       
   330                 portdict['device_id'] = prop.value
       
   331             elif (prop.name == 'OpenStack:device_owner'):
       
   332                 portdict['device_owner'] = prop.value
       
   333         portdict['security_groups'] = []
       
   334         portdict['status'] = 'ACTIVE'
       
   335         portdict['tenant_id'] = vport.tenantname
       
   336 
       
   337         return portdict
       
   338 
       
   339     def _apply_rsrc_props_filter(self, rsrclist, filters):
       
   340         # if all of the filter values are None, then return
       
   341         if all([value is None for value in filters.values()]):
       
   342             return
       
   343 
       
   344         rsrc_to_remove = []
       
   345         for rsrc in rsrclist:
       
   346             propdict = dict((prop.name, prop.value) for prop in rsrc.props)
       
   347             for key, value in filters.iteritems():
       
   348                 if value is None:
       
   349                     continue
       
   350                 if key not in propdict:
       
   351                     rsrc_to_remove.append(rsrc)
       
   352                     break
       
   353                 elif isinstance(value, list):
       
   354                     strlist = [str(v) for v in value]
       
   355                     if propdict[key] not in strlist:
       
   356                         rsrc_to_remove.append(rsrc)
       
   357                         break
       
   358                 # TODO(gmoodalb): - check if it's an instance of basestring?
       
   359                 elif propdict[key] != str(value):
       
   360                     rsrc_to_remove.append(rsrc)
       
   361                     break
       
   362 
       
   363         for rsrc in rsrc_to_remove:
       
   364             rsrclist.remove(rsrc)
       
   365 
       
   366     @lockutils.synchronized('evs-plugin', 'neutron-')
   203     @lockutils.synchronized('evs-plugin', 'neutron-')
   367     def evs_controller_addIPnet(self, tenantname, evsname, ipnetname, propstr):
   204     def _evs_controller_addIPnet(self, tenantname, evsname, ipnetname,
       
   205                                  propstr):
       
   206         LOG.debug(_("Adding IPnet: %s with properties: %s for tenant: %s "
       
   207                     "and for evs: %s") %
       
   208                   (ipnetname, propstr, tenantname, evsname))
       
   209 
       
   210         pat = radcli.ADRGlobPattern({'name': evsname,
       
   211                                      'tenant': tenantname})
   368         try:
   212         try:
   369             pat = radcli.ADRGlobPattern(
   213             evs = self.rad_connection.get_object(evsbind.EVS(), pat)
   370                 {'name': evsname, 'tenant': tenantname})
       
   371             evs = self._rc.get_object(evsbind.EVS(), pat)
       
   372             ipnet = evs.addIPnet(propstr, ipnetname)
   214             ipnet = evs.addIPnet(propstr, ipnetname)
   373         except radcli.ObjectError as oe:
   215         except radcli.ObjectError as oe:
   374             raise EVSControllerError(oe.get_payload().errmsg)
   216             raise EVSControllerError(oe.get_payload().errmsg)
   375         return ipnet
   217         return ipnet
       
   218 
       
   219     @lockutils.synchronized('evs-plugin', 'neutron-')
       
   220     def _evs_controller_updateIPnet(self, ipnetuuid, propstr):
       
   221         LOG.debug(_("Updating IPnet with id: %s with property string: %s") %
       
   222                   (ipnetuuid, propstr))
       
   223         pat = radcli.ADRGlobPattern({'uuid': ipnetuuid})
       
   224         try:
       
   225             ipnetlist = self.rad_connection.list_objects(evsbind.IPnet(), pat)
       
   226             if not ipnetlist:
       
   227                 return
       
   228             assert len(ipnetlist) == 1
       
   229             ipnet = self.rad_connection.get_object(ipnetlist[0])
       
   230             ipnet.setProperty(propstr)
       
   231         except radcli.ObjectError as oe:
       
   232             raise EVSControllerError(oe.get_payload().errmsg)
       
   233 
       
   234     def _subnet_pool_to_evs_pool(self, subnet):
       
   235         poolstr = ""
       
   236         # obtain the optional allocation pool
       
   237         pools = subnet.get('allocation_pools')
       
   238         if not pools or pools is attributes.ATTR_NOT_SPECIFIED:
       
   239             return poolstr
       
   240 
       
   241         for pool in pools:
       
   242             if poolstr:
       
   243                 poolstr += ","
       
   244             # if start and end address is same, EVS expects the address
       
   245             # to be provided as-is instead of x.x.x.x-x.x.x.x
       
   246             if pool['start'] == pool['end']:
       
   247                 poolstr += pool['start']
       
   248             else:
       
   249                 poolstr += "%s-%s" % (pool['start'], pool['end'])
       
   250         return poolstr
   376 
   251 
   377     def create_subnet(self, context, subnet):
   252     def create_subnet(self, context, subnet):
   378         """Creates a subnet(IPnet) for a given network(EVS).
   253         """Creates a subnet(IPnet) for a given network(EVS).
   379 
   254 
   380          An IP network represents a block of either IPv4 or IPv6 addresses
   255          An IP network represents a block of either IPv4 or IPv6 addresses
   381          (i.e., subnet) along with a default router for the block. Only one
   256          (i.e., subnet) along with a default router for the block. Only one
   382          IPnet can be associated with an EVS. All the zones/VNICs that
   257          IPnet can be associated with an EVS. All the zones/VNICs that
   383          connect to the EVS, through a VPort, will get an IP address from the
   258          connect to the EVS, through a VPort, will get an IP address from the
   384          IPnet associated with the EVS.
   259          IPnet associated with the EVS.
   385         """
   260         """
   386         ipnetname = subnet['subnet']['name']
   261 
   387         if not ipnetname:
   262         # TODO(gmoodalb): Take care of this now that we have pool.
   388             ipnetname = None
   263         # Even though EVS does not support allocation pools, it is OK for an
   389 
   264         # user to specify --allocation-pool because allocation pool management
   390         proplist = ['subnet=%s' % (subnet['subnet']['cidr'])]
   265         # is done by neutron-server and is transparent to EVS framework.
   391 
   266 
   392         # obtain the optional default router
   267         # user specified --no-gateway, and we don't support it
   393         defrouter = subnet['subnet']['gateway_ip']
   268         if subnet['subnet']['gateway_ip'] is None:
   394         if defrouter is None:
   269             raise EVSOpNotSupported(_("setting --no-gateway for a subnet "
   395             # user specified --no-gateway and we don't support it
   270                                       "not supported"))
   396             raise EVSOpNotSupported(_("cannot use --no-gateway"))
   271         if (subnet['subnet']['host_routes'] is not
   397 
   272                 attributes.ATTR_NOT_SPECIFIED):
   398         if defrouter is not attributes.ATTR_NOT_SPECIFIED:
   273             raise EVSOpNotSupported(_("setting --host-route for a subnet "
   399             proplist.append('defrouter=%s' % (defrouter))
   274                                       "not supported"))
   400 
   275 
   401         # obtain the optional allocation pool
   276         poolstr = self._subnet_pool_to_evs_pool(subnet['subnet'])
   402         pools = subnet['subnet']['allocation_pools']
   277 
   403         if pools is not attributes.ATTR_NOT_SPECIFIED:
   278         with context.session.begin(subtransactions=True):
   404             poolstr = ""
   279             # create the subnet in the DB
   405             for pool in pools:
   280             db_subnet = super(EVSNeutronPluginV2, self).create_subnet(context,
   406                 if poolstr:
   281                                                                       subnet)
   407                     poolstr += ","
   282             ipnetname = db_subnet['name']
   408                 # if start and end address is same, EVS expects the address
   283             if not ipnetname:
   409                 # to be provided as-is instead of x.x.x.x-x.x.x.x
   284                 ipnetname = None
   410                 if pool['start'] == pool['end']:
   285             evsname = db_subnet['network_id']
   411                     poolstr += pool['start']
   286             tenantname = db_subnet['tenant_id']
   412                 else:
   287             proplist = ['subnet=%s' % db_subnet['cidr']]
   413                     poolstr += "%s-%s" % (pool['start'], pool['end'])
   288             proplist.append('defrouter=%s' % db_subnet['gateway_ip'])
   414             proplist.append('pool=%s' % (poolstr))
   289             proplist.append('uuid=%s' % db_subnet['id'])
   415 
   290             if poolstr:
   416         # obtain the optional DNS nameservers
   291                 proplist.append('pool=%s' % (poolstr))
   417         nameservers = subnet['subnet']['dns_nameservers']
   292             self._evs_controller_addIPnet(tenantname, evsname, ipnetname,
   418         if attributes.is_attr_set(nameservers):
   293                                           ",".join(proplist))
   419             proplist.append('%s=%s' %
       
   420                             (SUBNET_IPNET_ATTRIBUTE_MAP['dns_nameservers'],
       
   421                              ','.join(nameservers)))
       
   422 
       
   423         # obtain the host routes
       
   424         hostroutes = subnet['subnet']['host_routes']
       
   425         if attributes.is_attr_set(hostroutes):
       
   426             hrlist = ['%s,%s' % (destination, nexthop)
       
   427                       for destination, nexthop in hostroutes]
       
   428             proplist.append('%s=%s' %
       
   429                             (SUBNET_IPNET_ATTRIBUTE_MAP['host_routes'],
       
   430                              ",".join(hrlist)))
       
   431 
       
   432         enable_dhcp = subnet['subnet']['enable_dhcp']
       
   433         proplist.append('%s=%s' %
       
   434                         (SUBNET_IPNET_ATTRIBUTE_MAP['enable_dhcp'],
       
   435                          enable_dhcp))
       
   436 
       
   437         propstr = None
       
   438         if proplist:
       
   439             propstr = ",".join(proplist)
       
   440 
       
   441         evsname = subnet['subnet']['network_id']
       
   442         tenantname = self._get_tenant_id_for_create(context, subnet['subnet'])
       
   443         ipnet = self.evs_controller_addIPnet(tenantname, evsname, ipnetname,
       
   444                                              propstr)
       
   445         retval = self._convert_ipnet_to_subnet(ipnet)
       
   446 
   294 
   447         # notify dhcp agent of subnet creation
   295         # notify dhcp agent of subnet creation
   448         self.dhcp_agent_notifier.notify(context, {'subnet': retval},
   296         self.dhcp_agent_notifier.notify(context, {'subnet': db_subnet},
   449                                         'subnet.create.end')
   297                                         'subnet.create.end')
   450         return retval
   298         return db_subnet
   451 
       
   452     @lockutils.synchronized('evs-plugin', 'neutron-')
       
   453     def evs_controller_updateIPnet(self, ipnetuuid, propstr):
       
   454         try:
       
   455             pat = radcli.ADRGlobPattern({'uuid': ipnetuuid})
       
   456             ipnetlist = self._rc.list_objects(evsbind.IPnet(), pat)
       
   457             assert len(ipnetlist) == 1
       
   458             ipnet = self._rc.get_object(ipnetlist[0])
       
   459             ipnet.setProperty(propstr)
       
   460         except radcli.ObjectError as oe:
       
   461             raise EVSControllerError(oe.get_payload().errmsg)
       
   462 
   299 
   463     def update_subnet(self, context, id, subnet):
   300     def update_subnet(self, context, id, subnet):
       
   301         LOG.debug(_("Updating Subnet: %s with %s") % (id, subnet))
   464         evs_rpccall_sync = subnet.pop('evs_rpccall_sync', False)
   302         evs_rpccall_sync = subnet.pop('evs_rpccall_sync', False)
   465         if not (set(subnet['subnet'].keys()) == set(('enable_dhcp',))):
   303         if (set(subnet['subnet'].keys()) - set(('enable_dhcp',
   466                 raise EVSOpNotSupported(_("only subnets with enable_dhcp "
   304                                                 'allocation_pools',
   467                                           "set can be updated"))
   305                                                 'ipv6_address_mode',
   468 
   306                                                 'ipv6_ra_mode'))):
   469         propstr = "%s=%s" % (SUBNET_IPNET_ATTRIBUTE_MAP['enable_dhcp'],
   307                 raise EVSOpNotSupported(_("only following subnet attributes "
   470                              subnet['subnet']['enable_dhcp'])
   308                                           "enable-dhcp, allocation-pool, "
   471         self.evs_controller_updateIPnet(id, propstr)
   309                                           "ipv6-address-mode, and "
   472         retval = self.get_subnet(context, id)
   310                                           "ipv6-ra-mode can be updated"))
       
   311 
       
   312         poolstr = self._subnet_pool_to_evs_pool(subnet['subnet'])
       
   313 
       
   314         with context.session.begin(subtransactions=True):
       
   315             # update subnet in DB
       
   316             retval = super(EVSNeutronPluginV2, self).\
       
   317                 update_subnet(context, id, subnet)
       
   318             # update EVS IPnet with allocation pool info
       
   319             if poolstr:
       
   320                 self._evs_controller_updateIPnet(id, "pool=%s" % poolstr)
   473 
   321 
   474         # notify dhcp agent of subnet update
   322         # notify dhcp agent of subnet update
   475         methodname = 'subnet.update.end'
   323         methodname = 'subnet.update.end'
   476         payload = {'subnet': retval}
   324         payload = {'subnet': retval}
   477         if not evs_rpccall_sync:
   325         if not evs_rpccall_sync:
   481                 methodname.replace(".", "_"), payload=payload)
   329                 methodname.replace(".", "_"), payload=payload)
   482             self.dhcp_agent_notifier.call(context, msg,
   330             self.dhcp_agent_notifier.call(context, msg,
   483                                           topic=topics.DHCP_AGENT)
   331                                           topic=topics.DHCP_AGENT)
   484         return retval
   332         return retval
   485 
   333 
   486     @lockutils.synchronized('evs-plugin', 'neutron-')
       
   487     def evs_controller_getIPnet(self, ipnetuuid):
       
   488         try:
       
   489             ipnetlist = self._evsc.getIPnetInfo('ipnet=%s' % (ipnetuuid))
       
   490         except radcli.ObjectError as oe:
       
   491             raise EVSControllerError(oe.get_payload().errmsg)
       
   492         return (ipnetlist[0] if ipnetlist else None)
       
   493 
       
   494     def get_subnet(self, context, id, fields=None):
   334     def get_subnet(self, context, id, fields=None):
   495         ipnet = self.evs_controller_getIPnet(id)
   335         LOG.debug(_("Getting subnet: %s"), id)
   496         if not ipnet:
   336         subnet = super(EVSNeutronPluginV2, self).get_subnet(context, id, None)
   497             return {}
   337         return self._fields(subnet, fields)
   498         subnetdict = self._convert_ipnet_to_subnet(ipnet)
       
   499         return self._fields(subnetdict, fields)
       
   500 
       
   501     @lockutils.synchronized('evs-plugin', 'neutron-')
       
   502     def evs_controller_getIPnetList(self, filterstr):
       
   503         try:
       
   504             ipnetlist = self._evsc.getIPnetInfo(filterstr)
       
   505         except radcli.ObjectError as oe:
       
   506             raise EVSControllerError(oe.get_payload().errmsg)
       
   507         return ipnetlist
       
   508 
   338 
   509     def get_subnets(self, context, filters=None, fields=None,
   339     def get_subnets(self, context, filters=None, fields=None,
   510                     sorts=None, limit=None, marker=None, page_reverse=False):
   340                     sorts=None, limit=None, marker=None, page_reverse=False):
   511 
   341         subnets = super(EVSNeutronPluginV2, self).\
   512         filterstr = None
   342             get_subnets(context, filters, None, sorts, limit, marker,
   513         # EVS desn't support filtering of resource based on
   343                         page_reverse)
   514         # properties, so we will have to filter ourselves
   344         return [self._fields(subnet, fields) for subnet in subnets]
   515         ipnet_props = {'OpenStack:enable_dhcp': None}
       
   516         if filters is not None:
       
   517             filterlist = []
       
   518             for key, value in filters.items():
       
   519                 if key == 'shared':
       
   520                     if any(value):
       
   521                         return []
       
   522                     continue
       
   523                 if key == 'verbose':
       
   524                     continue
       
   525                 if key == 'enable_dhcp':
       
   526                     ipnet_props[SUBNET_IPNET_ATTRIBUTE_MAP[key]] = value
       
   527                     continue
       
   528                 key = SUBNET_IPNET_ATTRIBUTE_MAP.get(key, key)
       
   529                 if isinstance(value, list):
       
   530                     value = ",".join(map(str, set(value)))
       
   531                     if not value:
       
   532                         continue
       
   533                 filterlist.append("%s=%s" % (key, value))
       
   534 
       
   535             if filterlist:
       
   536                 filterstr = ",".join(filterlist)
       
   537 
       
   538         LOG.debug(_("calling ListIPnet from get_subnets() filterstr: '%s'")
       
   539                   % (filterstr))
       
   540 
       
   541         ipnetlist = self.evs_controller_getIPnetList(filterstr)
       
   542         self._apply_rsrc_props_filter(ipnetlist, ipnet_props)
       
   543 
       
   544         retme = []
       
   545         for ipnet in ipnetlist:
       
   546             subnetdict = self._convert_ipnet_to_subnet(ipnet)
       
   547             retme.append(self._fields(subnetdict, fields))
       
   548 
       
   549         return retme
       
   550 
       
   551     def get_subnets_count(self, context, filters=None):
       
   552         return len(self.get_subnets(context, filters))
       
   553 
   345 
   554     def _release_subnet_dhcp_port(self, context, subnet, delete_network):
   346     def _release_subnet_dhcp_port(self, context, subnet, delete_network):
   555         """Release any dhcp port associated with the subnet"""
   347         """Release any dhcp port associated with the subnet"""
   556         filters = dict(evs=subnet['network_id'])
   348         filters = dict(network_id=[subnet['network_id']])
   557         portlist = self.get_ports(context, filters)
   349         portlist = self.get_ports(context, filters)
   558 
   350 
   559         if delete_network:
   351         if delete_network:
   560             # One can delete a network if there is only one port that has a
   352             # One can delete a network if there is only one port that has a
   561             # VNIC attached to it and that port happens to be a DHCP port.
   353             # VNIC attached to it and that port happens to be a DHCP port.
   565         else:
   357         else:
   566             # One can delete a subnet if there is only one port and that
   358             # One can delete a subnet if there is only one port and that
   567             # port happens to be a DHCP port.
   359             # port happens to be a DHCP port.
   568             update_subnet = len(portlist) == 1
   360             update_subnet = len(portlist) == 1
   569         if update_subnet:
   361         if update_subnet:
       
   362             # For IPv6 we need to first reset the IPv6 attributes
       
   363             if subnet['ip_version'] == 6:
       
   364                 if (attributes.is_attr_set(subnet.get('ipv6_address_mode'))):
       
   365                     subnet_update = {'subnet':
       
   366                                      {'ipv6_address_mode': None,
       
   367                                       'ipv6_ra_mode': None
       
   368                                       },
       
   369                                      'evs_rpccall_sync': True
       
   370                                      }
       
   371                     self.update_subnet(context, subnet['id'], subnet_update)
   570             # the lone port is a dhcp port created by dhcp agent
   372             # the lone port is a dhcp port created by dhcp agent
   571             # it must be released before we can delete the subnet
   373             # it must be released before we can delete the subnet
   572             subnet_update = {'subnet': {'enable_dhcp': False},
   374             subnet_update = {'subnet': {'enable_dhcp': False},
   573                              'evs_rpccall_sync': True}
   375                              'evs_rpccall_sync': True}
   574             self.update_subnet(context, subnet['id'], subnet_update)
   376             self.update_subnet(context, subnet['id'], subnet_update)
   575 
   377 
   576     @lockutils.synchronized('evs-plugin', 'neutron-')
   378     @lockutils.synchronized('evs-plugin', 'neutron-')
   577     def evs_controller_removeIPnet(self, tenantname, evsname, ipnetuuid):
   379     def _evs_controller_removeIPnet(self, tenantname, evsname, ipnetuuid):
       
   380         LOG.debug(_("Removing IPnet with id: %s for tenant: %s for evs: %s") %
       
   381                   (ipnetuuid, tenantname, evsname))
   578         pat = radcli.ADRGlobPattern({'name': evsname, 'tenant': tenantname})
   382         pat = radcli.ADRGlobPattern({'name': evsname, 'tenant': tenantname})
   579         try:
   383         try:
   580             evs = self._rc.get_object(evsbind.EVS(), pat)
   384             evs = self.rad_connection.get_object(evsbind.EVS(), pat)
   581             evs.removeIPnet(ipnetuuid)
   385             evs.removeIPnet(ipnetuuid)
   582         except radcli.ObjectError as oe:
   386         except radcli.ObjectError as oe:
       
   387             # '42' corresponds to EVS' EVS_ENOENT_IPNET error code
       
   388             if oe.get_payload().err == 42:
       
   389                 # EVS doesn't have that IPnet, return success to delete
       
   390                 # the IPnet from Neutron DB.
       
   391                 LOG.debug(_("IPnet could not be found in EVS."))
       
   392                 return
   583             raise EVSControllerError(oe.get_payload().errmsg)
   393             raise EVSControllerError(oe.get_payload().errmsg)
   584 
   394 
   585     def delete_subnet(self, context, id):
   395     def delete_subnet(self, context, id):
   586         subnet = self.get_subnet(context, id)
   396         subnet = self.get_subnet(context, id)
   587         if not subnet:
   397         if not subnet:
   594         # approach of updating the subnet's enable_dhcp attribute to
   404         # approach of updating the subnet's enable_dhcp attribute to
   595         # False that in turn sends a subnet.udpate notification. This
   405         # False that in turn sends a subnet.udpate notification. This
   596         # results in DHCP agent releasing the port.
   406         # results in DHCP agent releasing the port.
   597         if subnet['enable_dhcp']:
   407         if subnet['enable_dhcp']:
   598                 self._release_subnet_dhcp_port(context, subnet, False)
   408                 self._release_subnet_dhcp_port(context, subnet, False)
   599         self.evs_controller_removeIPnet(subnet['tenant_id'],
   409         with context.session.begin(subtransactions=True):
   600                                         subnet['network_id'], id)
   410             # delete subnet in DB
       
   411             super(EVSNeutronPluginV2, self).delete_subnet(context, id)
       
   412             self._evs_controller_removeIPnet(subnet['tenant_id'],
       
   413                                              subnet['network_id'], id)
   601 
   414 
   602         # notify dhcp agent
   415         # notify dhcp agent
   603         payload = {
   416         payload = {
   604             'subnet': {
   417             'subnet': {
   605                 'network_id': subnet['network_id'],
   418                 'network_id': subnet['network_id'],
   607             }
   420             }
   608         }
   421         }
   609         self.dhcp_agent_notifier.notify(context, payload, 'subnet.delete.end')
   422         self.dhcp_agent_notifier.notify(context, payload, 'subnet.delete.end')
   610 
   423 
   611     @lockutils.synchronized('evs-plugin', 'neutron-')
   424     @lockutils.synchronized('evs-plugin', 'neutron-')
   612     def evs_controller_createEVS(self, tenantname, evsname, propstr):
   425     def _evs_controller_createEVS(self, tenantname, evsname, propstr):
       
   426         LOG.debug(_("Adding EVS: %s with properties: %s for tenant: %s") %
       
   427                   (evsname, propstr, tenantname))
   613         try:
   428         try:
   614             evs = self._evsc.createEVS(propstr, tenantname, evsname)
   429             evs = self.rad_connection.\
       
   430                 get_object(evsbind.EVSController()).\
       
   431                 createEVS(propstr, tenantname, evsname)
   615         except radcli.ObjectError as oe:
   432         except radcli.ObjectError as oe:
   616             raise EVSControllerError(oe.get_payload().errmsg)
   433             raise EVSControllerError(oe.get_payload().errmsg)
   617         return evs
   434         return evs
       
   435 
       
   436     def _extend_network_dict(self, network, evs):
       
   437         for prop in evs.props:
       
   438             if prop.name == 'l2-type':
       
   439                 network[providernet.NETWORK_TYPE] = prop.value
       
   440             elif prop.name == 'vlanid' or prop.name == 'vni':
       
   441                 network[providernet.SEGMENTATION_ID] = int(prop.value)
   618 
   442 
   619     def create_network(self, context, network):
   443     def create_network(self, context, network):
   620         """Creates a network(EVS) for a given tenant.
   444         """Creates a network(EVS) for a given tenant.
   621 
   445 
   622         An Elastic Virtual Switch (EVS) is a virtual switch that spans
   446         An Elastic Virtual Switch (EVS) is a virtual switch that spans
   624         segment, and the isolation is implemented either through VLANs or
   448         segment, and the isolation is implemented either through VLANs or
   625         VXLANs. An EVS provides network connectivity between the Virtual
   449         VXLANs. An EVS provides network connectivity between the Virtual
   626         Machines connected to it. There are two main resources associated with
   450         Machines connected to it. There are two main resources associated with
   627         an EVS: IPnet and VPort.
   451         an EVS: IPnet and VPort.
   628         """
   452         """
       
   453 
       
   454         if network['network']['admin_state_up'] is False:
       
   455             raise EVSOpNotSupported(_("setting admin_state_up=False for a "
       
   456                                       "network not supported"))
       
   457 
       
   458         if network['network']['shared'] is True:
       
   459             raise EVSOpNotSupported(_("setting shared=True for a "
       
   460                                       "network not supported"))
       
   461 
   629         evsname = network['network']['name']
   462         evsname = network['network']['name']
   630         if not evsname:
   463         if not evsname:
   631             evsname = None
   464             evsname = None
   632 
   465 
   633         tenantname = self._get_tenant_id_for_create(context,
   466         tenantname = self._get_tenant_id_for_create(context,
   652             else:
   485             else:
   653                 raise EVSControllerError(_("specified "
   486                 raise EVSControllerError(_("specified "
   654                                            "provider:network_type '%s' not "
   487                                            "provider:network_type '%s' not "
   655                                            "supported") % network_type)
   488                                            "supported") % network_type)
   656 
   489 
   657         router_external = network['network'][external_net.EXTERNAL]
       
   658         if attributes.is_attr_set(router_external):
       
   659             proplist.append("%s=%s" %
       
   660                             (NETWORK_EVS_ATTRIBUTE_MAP[external_net.EXTERNAL],
       
   661                              router_external))
       
   662 
       
   663         propstr = None
   490         propstr = None
   664         if proplist:
   491         if proplist:
   665             propstr = ",".join(proplist)
   492             propstr = ",".join(proplist)
   666 
   493 
   667         evs = self.evs_controller_createEVS(tenantname, evsname, propstr)
   494         with context.session.begin(subtransactions=True):
   668         return self._convert_evs_to_network(evs)
   495             # create the network in DB
       
   496             net = super(EVSNeutronPluginV2, self).create_network(context,
       
   497                                                                  network)
       
   498             self._process_l3_create(context, net, network['network'])
       
   499             # if --router:external is not set, the above function does
       
   500             # not update net with router:external set to False
       
   501             if net.get(external_net.EXTERNAL) is None:
       
   502                 net[external_net.EXTERNAL] = False
       
   503 
       
   504             # create EVS on the EVS controller
       
   505             if propstr:
       
   506                 propstr += ",uuid=%s" % net['id']
       
   507             else:
       
   508                 propstr = "uuid=%s" % net['id']
       
   509             evs = self._evs_controller_createEVS(tenantname, evsname, propstr)
       
   510 
       
   511             # add provider information into net
       
   512             self._extend_network_dict(net, evs)
       
   513 
       
   514         return net
   669 
   515 
   670     def update_network(self, context, id, network):
   516     def update_network(self, context, id, network):
   671         raise EVSOpNotSupported(_("net-update"))
   517         raise EVSOpNotSupported(_("net-update"))
   672 
   518 
   673     @lockutils.synchronized('evs-plugin', 'neutron-')
   519     @lockutils.synchronized('evs-plugin', 'neutron-')
   674     def evs_controller_getEVS(self, evsuuid):
   520     def _evs_controller_getEVS(self, evsuuid):
       
   521         LOG.debug(_("Getting EVS: %s"), evsuuid)
   675         try:
   522         try:
   676             evslist = self._evsc.getEVSInfo('evs=%s' % evsuuid)
   523             evslist = self.rad_connection.\
       
   524                 get_object(evsbind.EVSController()).\
       
   525                 getEVSInfo('evs=%s' % evsuuid)
   677         except radcli.ObjectError as oe:
   526         except radcli.ObjectError as oe:
   678             raise EVSControllerError(oe.getpayload().errmsg)
   527             raise EVSControllerError(oe.getpayload().errmsg)
   679         return (evslist[0] if evslist else None)
   528         if not evslist:
       
   529             LOG.error(_("EVS framework does not have Neutron network "
       
   530                         "'%s' defined"), evsuuid)
       
   531             return None
       
   532         return evslist[0]
   680 
   533 
   681     def get_network(self, context, id, fields=None):
   534     def get_network(self, context, id, fields=None):
   682         evs = self.evs_controller_getEVS(id)
   535         with context.session.begin(subtransactions=True):
   683         if not evs:
   536             net = super(EVSNeutronPluginV2, self).get_network(context,
   684             return {}
   537                                                               id, None)
   685         networkdict = self._convert_evs_to_network(evs)
   538             # call EVS controller to get provider network information
   686         return self._fields(networkdict, fields)
   539             evs = self._evs_controller_getEVS(net['id'])
   687 
   540             if evs:
   688     @lockutils.synchronized('evs-plugin', 'neutron-')
   541                 self._extend_network_dict(net, evs)
   689     def evs_controller_getEVSList(self, filterstr):
   542         return self._fields(net, fields)
   690         try:
       
   691             evslist = self._evsc.getEVSInfo(filterstr)
       
   692         except radcli.ObjectError as oe:
       
   693             raise EVSControllerError(oe.get_payload().errmsg)
       
   694         return evslist
       
   695 
   543 
   696     def get_networks(self, context, filters=None, fields=None,
   544     def get_networks(self, context, filters=None, fields=None,
   697                      sorts=None, limit=None, marker=None, page_reverse=False):
   545                      sorts=None, limit=None, marker=None, page_reverse=False):
   698 
   546 
   699         filterstr = None
   547         with context.session.begin(subtransactions=True):
   700         # EVS desn't support filtering of resource based on
   548             nets = super(EVSNeutronPluginV2, self).\
   701         # properties, so we will have to filter ourselves
   549                 get_networks(context, filters, None, sorts, limit, marker,
   702         evs_props = {'OpenStack:router:external': None}
   550                              page_reverse)
   703         if filters is not None:
   551             for net in nets:
   704             filterlist = []
   552                 evs = self._evs_controller_getEVS(net['id'])
   705             for key, value in filters.items():
   553                 if evs:
   706                 if key == 'shared':
   554                     self._extend_network_dict(net, evs)
   707                     if any(value):
   555         return [self._fields(net, fields) for net in nets]
   708                         # EVS doesn't support shared networks
       
   709                         return []
       
   710                     continue
       
   711                 if key in ('admin_state_up', 'verbose'):
       
   712                     continue
       
   713                 if key == 'router:external':
       
   714                     evs_props[NETWORK_EVS_ATTRIBUTE_MAP[key]] = value
       
   715                     continue
       
   716                 key = NETWORK_EVS_ATTRIBUTE_MAP.get(key, key)
       
   717                 if isinstance(value, list):
       
   718                     value = ",".join(map(str, set(value)))
       
   719                     if not value:
       
   720                         continue
       
   721                 filterlist.append("%s=%s" % (key, value))
       
   722 
       
   723             if filterlist:
       
   724                 filterstr = ",".join(filterlist)
       
   725 
       
   726         LOG.debug(_("calling ListEVswitch from get_networks(): '%s'")
       
   727                   % (filterstr))
       
   728         evslist = self.evs_controller_getEVSList(filterstr)
       
   729         self._apply_rsrc_props_filter(evslist, evs_props)
       
   730 
       
   731         retme = []
       
   732         for evs in evslist:
       
   733             networkdict = self._convert_evs_to_network(evs)
       
   734             retme.append(self._fields(networkdict, fields))
       
   735 
       
   736         return retme
       
   737 
       
   738     def get_networks_count(self, context, filters=None):
       
   739         return len(self.get_networks(context, filters))
       
   740 
   556 
   741     @lockutils.synchronized('evs-plugin', 'neutron-')
   557     @lockutils.synchronized('evs-plugin', 'neutron-')
   742     def evs_controller_deleteEVS(self, tenantname, evsuuid):
   558     def _evs_controller_deleteEVS(self, tenantname, evsuuid):
       
   559         LOG.debug(_("Removing EVS with id: %s for tenant: %s") %
       
   560                   (evsuuid, tenantname))
   743         try:
   561         try:
   744             self._evsc.deleteEVS(evsuuid, tenantname)
   562             self.rad_connection.\
       
   563                 get_object(evsbind.EVSController()).\
       
   564                 deleteEVS(evsuuid, tenantname)
   745         except radcli.ObjectError as oe:
   565         except radcli.ObjectError as oe:
       
   566             # '41' corresponds to EVS' EVS_ENOENT_EVS error code
       
   567             if oe.get_payload().err == 41:
       
   568                 # EVS doesn't have that EVS, return success to delete
       
   569                 # the EVS from Neutron DB.
       
   570                 LOG.debug(_("EVS could not be found in EVS backend."))
       
   571                 return
   746             raise EVSControllerError(oe.get_payload().errmsg)
   572             raise EVSControllerError(oe.get_payload().errmsg)
   747 
   573 
   748     def delete_network(self, context, id):
   574     def delete_network(self, context, id):
   749         # Check if it is an external network and whether addresses in that
   575         # Check if it is an external network and whether addresses in that
   750         # network are being used for floating ips
   576         # network are being used for floating ips
   751         evs = self.get_network(context, id)
   577         net = self.get_network(context, id)
   752         if evs[external_net.EXTERNAL]:
   578         if net[external_net.EXTERNAL]:
   753             filters = dict(evs=id)
   579             filters = dict(network_id=[id])
   754             portlist = self.get_ports(context, filters)
   580             portlist = self.get_ports(context, filters)
   755             ports_with_deviceid = [port for port in portlist
   581             ports_with_deviceid = [port for port in portlist
   756                                    if port['device_id'] != '']
   582                                    if port['device_id'] != '']
   757             if ports_with_deviceid:
   583             if ports_with_deviceid:
   758                 raise exceptions.NetworkInUse(net_id=id)
   584                 raise exceptions.NetworkInUse(net_id=id)
   759         filters = dict(network_id=id)
   585         filters = dict(network_id=[id])
   760         subnets = self.get_subnets(context, filters=filters)
   586         subnets = self.get_subnets(context, filters=filters)
   761         dhcp_subnets = [s for s in subnets if s['enable_dhcp']]
   587         dhcp_subnets = [s for s in subnets if s['enable_dhcp']]
   762         for subnet in dhcp_subnets:
   588         for subnet in dhcp_subnets:
   763             self._release_subnet_dhcp_port(context, subnet, True)
   589             self._release_subnet_dhcp_port(context, subnet, True)
   764         self.evs_controller_deleteEVS(context.tenant_id, id)
   590         with context.session.begin(subtransactions=True):
       
   591             super(EVSNeutronPluginV2, self).delete_network(context, id)
       
   592             self._evs_controller_deleteEVS(net['tenant_id'], id)
   765 
   593 
   766         # notify dhcp agent of network deletion
   594         # notify dhcp agent of network deletion
   767         self.dhcp_agent_notifier.notify(context, {'network': {'id': id}},
   595         self.dhcp_agent_notifier.notify(context, {'network': {'id': id}},
   768                                         'network.delete.end')
   596                                         'network.delete.end')
   769 
   597 
   770     @lockutils.synchronized('evs-plugin', 'neutron-')
   598     @lockutils.synchronized('evs-plugin', 'neutron-')
   771     def evs_controller_addVPort(self, tenantname, evsname, vportname, propstr):
   599     def _evs_controller_addVPort(self, tenantname, evsname, vportname,
       
   600                                  propstr):
       
   601         LOG.debug(_("Adding VPort: %s with properties: %s for tenant: %s "
       
   602                     "and for evs: %s") %
       
   603                   (vportname, propstr, tenantname, evsname))
       
   604 
   772         try:
   605         try:
   773             pat = radcli.ADRGlobPattern({'name': evsname,
   606             pat = radcli.ADRGlobPattern({'name': evsname,
   774                                          'tenant': tenantname})
   607                                          'tenant': tenantname})
   775             evs = self._rc.get_object(evsbind.EVS(), pat)
   608             evs = self.rad_connection.get_object(evsbind.EVS(), pat)
   776             vport = evs.addVPort(propstr, vportname)
   609             vport = evs.addVPort(propstr, vportname)
   777         except radcli.ObjectError as oe:
   610         except radcli.ObjectError as oe:
   778             raise EVSControllerError(oe.get_payload().errmsg)
   611             raise EVSControllerError(oe.get_payload().errmsg)
   779         return vport
   612         return vport
   780 
   613 
   787              -- IP address and
   620              -- IP address and
   788              -- MAC address, et al
   621              -- MAC address, et al
   789          This configuration is inherited by the VNIC when it connects to the
   622          This configuration is inherited by the VNIC when it connects to the
   790          VPort.
   623          VPort.
   791         """
   624         """
   792         vportname = port['port']['name']
   625         if port['port']['admin_state_up'] is False:
   793         if not vportname:
   626             raise EVSOpNotSupported(_("setting admin_state_up=False for a "
   794             vportname = None
   627                                       "port not supported"))
   795 
   628 
   796         proplist = []
   629         with context.session.begin(subtransactions=True):
   797         macaddr = port['port']['mac_address']
   630             # for external gateway ports and floating ips, tenant_id
   798         if attributes.is_attr_set(macaddr):
   631             # is not set, but EVS does not like it.
   799             proplist.append('macaddr=%s' % (macaddr))
   632             tenant_id = self._get_tenant_id_for_create(context, port['port'])
   800 
   633             if not tenant_id:
   801         fixed_ips = port['port']['fixed_ips']
   634                 network = self.get_network(context, port['port']['network_id'])
   802         if attributes.is_attr_set(fixed_ips):
   635                 port['port']['tenant_id'] = network['tenant_id']
   803             # we only support one subnet
   636             # create the port in the DB
   804             ipaddr = fixed_ips[0].get('ip_address')
   637             db_port = super(EVSNeutronPluginV2, self).create_port(context,
   805             if ipaddr is not None:
   638                                                                   port)
   806                 proplist.append('ipaddr=%s' % ipaddr)
   639 
   807 
   640             tenantname = db_port['tenant_id']
   808         # retrieve device_id and device_owner
   641             vportname = db_port['name']
   809         device_id = port['port']['device_id']
   642             if not vportname:
   810         if attributes.is_attr_set(device_id) and device_id:
   643                 vportname = None
   811             proplist.append("%s=%s" %
   644             evs_id = db_port['network_id']
   812                             (PORT_VPORT_ATTRIBUTE_MAP['device_id'], device_id))
   645             proplist = ['macaddr=%s' % db_port['mac_address']]
   813 
   646             proplist.append('ipaddr=%s' %
   814         device_owner = port['port']['device_owner']
   647                             db_port['fixed_ips'][0].get('ip_address'))
   815         if attributes.is_attr_set(device_owner) and device_owner:
   648             proplist.append('uuid=%s' % db_port['id'])
   816             proplist.append("%s=%s" %
   649 
   817                             (PORT_VPORT_ATTRIBUTE_MAP['device_owner'],
   650             self._evs_controller_addVPort(tenantname, evs_id, vportname,
   818                              device_owner))
   651                                           ",".join(proplist))
   819 
       
   820         propstr = None
       
   821         if proplist:
       
   822             propstr = ",".join(proplist)
       
   823 
       
   824         evsname = port['port']['network_id']
       
   825         tenantname = self._get_tenant_id_for_create(context, port['port'])
       
   826         if not tenantname:
       
   827             network = self.get_network(context, evsname)
       
   828             tenantname = network['tenant_id']
       
   829         vport = self.evs_controller_addVPort(tenantname, evsname, vportname,
       
   830                                              propstr)
       
   831         retval = self._convert_vport_to_port(context, vport)
       
   832 
   652 
   833         # notify dhcp agent of port creation
   653         # notify dhcp agent of port creation
   834         self.dhcp_agent_notifier.notify(context, {'port': retval},
   654         self.dhcp_agent_notifier.notify(context, {'port': db_port},
   835                                         'port.create.end')
   655                                         'port.create.end')
   836         return retval
   656         return db_port
   837 
       
   838     @lockutils.synchronized('evs-plugin', 'neutron-')
       
   839     def evs_controller_updateVPort(self, vportuuid, proplist):
       
   840         try:
       
   841             pat = radcli.ADRGlobPattern({'uuid': vportuuid})
       
   842             vportlist = self._rc.list_objects(evsbind.VPort(), pat)
       
   843             assert len(vportlist) == 1
       
   844             vport = self._rc.get_object(vportlist[0])
       
   845             for prop in proplist:
       
   846                 vport.setProperty(prop)
       
   847         except radcli.ObjectError as oe:
       
   848             raise EVSControllerError(oe.get_payload().errmsg)
       
   849 
   657 
   850     def update_port(self, context, id, port):
   658     def update_port(self, context, id, port):
   851         # EVS does not allow updating certain attributes
   659         # EVS does not allow updating certain attributes, so check for it
   852         if not (set(port['port'].keys()) <=
   660         state = port['port'].get('admin_state_up')
   853                 set(('device_id', 'device_owner'))):
   661         if state and state is False:
   854             raise EVSOpNotSupported(_("only device_id and "
   662             raise EVSOpNotSupported(_("updating port's admin_state_up to "
   855                                       "device_owner supported"))
   663                                       "False is not supported"))
   856 
   664 
   857         proplist = []
   665         # Get the original port and fail if any attempt is being made
   858         device_id = port['port'].get('device_id')
   666         # to change fixed_ips of the port since EVS doesn't support it
   859         if device_id is not None:
   667         original_port = super(EVSNeutronPluginV2, self).get_port(context, id)
   860             # EVS expects property values to be non-zero length
   668         original_ips = original_port['fixed_ips']
   861             if len(device_id) == 0:
   669         update_ips = port['port'].get('fixed_ips')
   862                 device_id = " "
   670         if (update_ips and
   863             proplist.append("%s=%s" %
   671             (len(update_ips) != 1 or
   864                             (PORT_VPORT_ATTRIBUTE_MAP['device_id'], device_id))
   672              update_ips[0]['subnet_id'] != original_ips[0]['subnet_id'] or
   865 
   673              update_ips[0]['ip_address'] != original_ips[0]['ip_address'])):
   866         device_owner = port['port'].get('device_owner')
   674             raise EVSOpNotSupported(_("updating port's fixed_ips "
   867         if device_owner is not None:
   675                                       "is not supported"))
   868             if len(device_owner) == 0:
   676         LOG.debug(_("Updating port %s with %s") % (id, port))
   869                 device_owner = " "
   677         db_port = super(EVSNeutronPluginV2, self).update_port(context,
   870             proplist.append("%s=%s" %
   678                                                               id, port)
   871                             (PORT_VPORT_ATTRIBUTE_MAP['device_owner'],
       
   872                              device_owner))
       
   873 
       
   874         if not proplist:
       
   875             return dict()
       
   876 
       
   877         self.evs_controller_updateVPort(id, proplist)
       
   878         retval = self.get_port(context, id)
       
   879 
   679 
   880         # notify dhcp agent of port update
   680         # notify dhcp agent of port update
   881         self.dhcp_agent_notifier.notify(context, {'port': retval},
   681         self.dhcp_agent_notifier.notify(context, {'port': db_port},
   882                                         'port.update.end')
   682                                         'port.update.end')
   883         return retval
   683         return db_port
   884 
       
   885     @lockutils.synchronized('evs-plugin', 'neutron-')
       
   886     def evs_controller_getVPort(self, vportuuid):
       
   887         try:
       
   888             vportlist = self._evsc.getVPortInfo('vport=%s' % (vportuuid))
       
   889         except radcli.ObjectError as oe:
       
   890             raise EVSControllerError(oe.get_payload().errmsg)
       
   891         return (vportlist[0] if vportlist else None)
       
   892 
   684 
   893     def get_port(self, context, id, fields=None):
   685     def get_port(self, context, id, fields=None):
   894         vport = self.evs_controller_getVPort(id)
   686         LOG.debug(_("Getting port: %s"), id)
   895         if not vport:
   687         port = super(EVSNeutronPluginV2, self).get_port(context, id, None)
   896             return {}
   688         return self._fields(port, fields)
   897         portdict = self._convert_vport_to_port(context, vport)
       
   898         return self._fields(portdict, fields)
       
   899 
       
   900     @lockutils.synchronized('evs-plugin', 'neutron-')
       
   901     def evs_controller_getVPortList(self, filterstr):
       
   902         try:
       
   903             vportlist = self._evsc.getVPortInfo(filterstr)
       
   904         except radcli.ObjectError as oe:
       
   905             raise EVSControllerError(oe.get_payload().errmsg)
       
   906         return vportlist
       
   907 
   689 
   908     def get_ports(self, context, filters=None, fields=None,
   690     def get_ports(self, context, filters=None, fields=None,
   909                   sorts=None, limit=None, marker=None, page_reverse=False):
   691                   sorts=None, limit=None, marker=None, page_reverse=False):
   910         LOG.debug(_("inside the get_ports() method: filters: '%s'") %
   692         ports = super(EVSNeutronPluginV2, self).\
   911                   str(filters))
   693             get_ports(context, filters, None, sorts, limit, marker,
   912 
   694                       page_reverse)
   913         filterstr = None
   695         return [self._fields(port, fields) for port in ports]
   914         # EVS desn't support filtering of resource based on
   696 
   915         # properties, so we will have to filter ourselves
   697     def notify_l3agent(self, context, port):
   916         vport_props = {'OpenStack:device_id': None,
       
   917                        'OpenStack:device_owner': None}
       
   918         if filters is not None:
       
   919             filterlist = []
       
   920             for key, value in filters.items():
       
   921                 if key == 'shared':
       
   922                     if any(value):
       
   923                         return []
       
   924                     continue
       
   925                 if key == 'admin_state_up':
       
   926                     continue
       
   927                 if key in ('device_id', 'device_owner'):
       
   928                     vport_props[PORT_VPORT_ATTRIBUTE_MAP[key]] = value
       
   929                     continue
       
   930                 key = PORT_VPORT_ATTRIBUTE_MAP.get(key, key)
       
   931                 if isinstance(value, list):
       
   932                     value = ",".join(map(str, set(value)))
       
   933                     if not value:
       
   934                         continue
       
   935                 filterlist.append("%s=%s" % (key, value))
       
   936 
       
   937             if filterlist:
       
   938                 filterstr = ",".join(filterlist)
       
   939 
       
   940         LOG.debug(_("calling getVPortInfo from get_ports(): '%s'") %
       
   941                   (filterstr))
       
   942         vportlist = self.evs_controller_getVPortList(filterstr)
       
   943         self._apply_rsrc_props_filter(vportlist, vport_props)
       
   944 
       
   945         retme = []
       
   946         for vport in vportlist:
       
   947             portdict = self._convert_vport_to_port(context, vport)
       
   948             retme.append(self._fields(portdict, fields))
       
   949 
       
   950         return retme
       
   951 
       
   952     def get_ports_count(self, context, filters=None):
       
   953         return len(self.get_ports(context, filters))
       
   954 
       
   955     def _release_l3agent_internal_port(self, context, port):
       
   956         """ If an L3 agent is using this port, then we need to send
   698         """ If an L3 agent is using this port, then we need to send
   957         a notification to L3 agent to release the port before we can
   699         a notification to the L3 agent so that it can remove the EVS VPort
   958         delete the port"""
   700         associated with the Neutron Port. In that case, the EVS Plugin will
       
   701         only remove the Neutron port from the DB, so return False.
       
   702 
       
   703         If the port is not used by the L3 agent, then the EVS plugin
       
   704         will remove both the Neutron port and EVS VPort, so return True.
       
   705         """
   959 
   706 
   960         device_owner = port['device_owner']
   707         device_owner = port['device_owner']
   961         if device_owner not in [l3_constants.DEVICE_OWNER_ROUTER_INTF,
   708         if device_owner not in [constants.DEVICE_OWNER_ROUTER_INTF,
   962                                 l3_constants.DEVICE_OWNER_ROUTER_GW,
   709                                 constants.DEVICE_OWNER_ROUTER_GW,
   963                                 l3_constants.DEVICE_OWNER_FLOATINGIP]:
   710                                 constants.DEVICE_OWNER_FLOATINGIP]:
   964             return
   711             return True
   965         router_id = port['device_id']
   712         router_id = port['device_id']
   966         port_update = {
   713         port_update = {
   967             'port': {
   714             'port': {
   968                 'device_id': '',
   715                 'device_id': '',
   969                 'device_owner': ''
   716                 'device_owner': ''
   970             }
   717             }
   971         }
   718         }
   972         self.update_port(context, port['id'], port_update)
   719         self.update_port(context, port['id'], port_update)
   973         if device_owner in [l3_constants.DEVICE_OWNER_ROUTER_INTF,
   720         if device_owner in [constants.DEVICE_OWNER_ROUTER_INTF,
   974                             l3_constants.DEVICE_OWNER_ROUTER_GW]:
   721                             constants.DEVICE_OWNER_ROUTER_GW]:
   975             msg = l3_rpc_agent_api.L3AgentNotify.make_msg("routers_updated",
   722             self.l3_agent_notifier.routers_updated(context, [router_id])
   976                                                           routers=[router_id])
   723             return False
   977             l3_rpc_agent_api.L3AgentNotify.call(context, msg,
   724         return True
   978                                                 topic=topics.L3_AGENT)
       
   979 
   725 
   980     @lockutils.synchronized('evs-plugin', 'neutron-')
   726     @lockutils.synchronized('evs-plugin', 'neutron-')
   981     def evs_controller_removeVPort(self, tenantname, evsname, vportuuid,
   727     def _evs_controller_removeVPort(self, tenantname, evsname, vportuuid):
   982                                    vportname):
   728         LOG.debug(_("Removing VPort with id: %s for tenant: %s for evs: %s") %
       
   729                   (vportuuid, tenantname, evsname))
   983         pat = radcli.ADRGlobPattern({'name': evsname,
   730         pat = radcli.ADRGlobPattern({'name': evsname,
   984                                      'tenant': tenantname})
   731                                      'tenant': tenantname})
   985         try:
   732         try:
   986             evs = self._rc.get_object(evsbind.EVS(), pat)
   733             evs = self.rad_connection.get_object(evsbind.EVS(), pat)
   987             evs.removeVPort(vportuuid)
   734             evs.removeVPort(vportuuid)
   988         except radcli.ObjectError as oe:
   735         except radcli.ObjectError as oe:
   989             # '7' corresponds to EVS' EVS_EBUSY_VPORT error code
   736             # '7' corresponds to EVS' EVS_EBUSY_VPORT error code
   990             if oe.get_payload().err == 7:
   737             if oe.get_payload().err == 7:
   991                 # It is possible that the VM is destroyed, but EVS is unaware
   738                 # It is possible that the VM is destroyed, but EVS is unaware
   992                 # of it. So, try to reset the vport. If it succeeds, then call
   739                 # of it. So, try to reset the vport. If it succeeds, then call
   993                 # removeVPort() again.
   740                 # removeVPort() again.
       
   741                 LOG.debug(_("EVS VPort is busy. We will need to reset "
       
   742                             "and then remove"))
   994                 try:
   743                 try:
   995                     evs.resetVPort(vportname)
   744                     evs.resetVPort(vportuuid)
   996                     evs.removeVPort(vportuuid)
   745                     evs.removeVPort(vportuuid)
   997                 except:
   746                 except:
   998                     # we failed one of the above operations, just return
   747                     # we failed one of the above operations, just return
   999                     # the original exception.
   748                     # the original exception.
  1000                     pass
   749                     pass
  1001                 else:
   750                 else:
  1002                     # the reset and remove succeeded, just return.
   751                     # the reset and remove succeeded, just return.
  1003                     return
   752                     return
       
   753             # '43' corresponds to EVS' EVS_ENOENT_VPORT error code
       
   754             elif oe.get_payload().err == 43:
       
   755                 # EVS doesn't have that VPort, return success to delete
       
   756                 # the VPort from Neutron DB.
       
   757                 LOG.debug(_("VPort could not be found in EVS."))
       
   758                 return
  1004             raise EVSControllerError(oe.get_payload().errmsg)
   759             raise EVSControllerError(oe.get_payload().errmsg)
  1005 
   760 
  1006     def delete_port(self, context, id, l3_port_check=True):
   761     def delete_port(self, context, id, l3_port_check=True):
  1007         if l3_port_check:
   762         if l3_port_check:
  1008             self.prevent_l3_port_deletion(context, id)
   763             self.prevent_l3_port_deletion(context, id)
  1009         self.disassociate_floatingips(context, id)
   764         self.disassociate_floatingips(context, id)
  1010         port = self.get_port(context, id)
   765         port = self.get_port(context, id)
  1011         if not port:
   766         if not port:
  1012             return
   767             return
  1013         if not l3_port_check:
   768         del_vport = l3_port_check or self.notify_l3agent(context, port)
  1014             self._release_l3agent_internal_port(context, port)
   769         with context.session.begin(subtransactions=True):
  1015         self.evs_controller_removeVPort(port['tenant_id'], port['network_id'],
   770             super(EVSNeutronPluginV2, self).delete_port(context, id)
  1016                                         id, port['name'])
   771             if del_vport:
       
   772                 self._evs_controller_removeVPort(port['tenant_id'],
       
   773                                                  port['network_id'],
       
   774                                                  port['id'])
  1017 
   775 
  1018         # notify dhcp agent of port deletion
   776         # notify dhcp agent of port deletion
  1019         payload = {
   777         payload = {
  1020             'port': {
   778             'port': {
  1021                 'network_id': port['network_id'],
   779                 'network_id': port['network_id'],
  1022                 'id': id,
   780                 'id': id,
  1023             }
   781             }
  1024         }
   782         }
  1025         self.dhcp_agent_notifier.notify(context, payload, 'port.delete.end')
   783         self.dhcp_agent_notifier.notify(context, payload, 'port.delete.end')
  1026 
       
  1027     # needed for DHCP agent support
       
  1028     def update_fixed_ip_lease_expiration(self, context, network_id,
       
  1029                                          ip_address, lease_remaining):
       
  1030         pass
       
  1031 
       
  1032     # needed for L3 agent support
       
  1033     def _get_network(self, context, network_id):
       
  1034         return self.get_network(context, network_id)
       
  1035 
       
  1036     def _get_subnet(self, context, subnet_id):
       
  1037         return self.get_subnet(context, subnet_id)
       
  1038 
       
  1039     def _get_port(self, context, port_id):
       
  1040         return self.get_port(context, port_id)
       
  1041 
       
  1042     def _delete_port(self, context, port_id):
       
  1043         return self.delete_port(context, port_id)
       
  1044 
       
  1045     def _get_subnets_by_network(self, context, network_id):
       
  1046         return self.get_subnets(context, filters={'network_id': network_id})
       
  1047 
       
  1048     def _network_is_external(self, context, net_id):
       
  1049         try:
       
  1050             evs = self.get_network(context, net_id)
       
  1051             return evs[external_net.EXTERNAL]
       
  1052         except:
       
  1053             return False