components/openstack/neutron/files/evs/plugin.py
changeset 6848 8e252a37ed0d
parent 6847 57069587975f
child 6849 f9a2279efa0d
equal deleted inserted replaced
6847:57069587975f 6848:8e252a37ed0d
     1 # vim: tabstop=4 shiftwidth=4 softtabstop=4
       
     2 
       
     3 # Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
       
     4 #
       
     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
       
     7 #    a copy of the License at
       
     8 #
       
     9 #         http://www.apache.org/licenses/LICENSE-2.0
       
    10 #
       
    11 #    Unless required by applicable law or agreed to in writing, software
       
    12 #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
       
    13 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
       
    14 #    License for the specific language governing permissions and limitations
       
    15 #    under the License.
       
    16 #
       
    17 # @author: Girish Moodalbail, Oracle, Inc.
       
    18 
       
    19 import rad.client as radcli
       
    20 import rad.connect as radcon
       
    21 import rad.bindings.com.oracle.solaris.rad.evscntl_1 as evsbind
       
    22 
       
    23 from oslo_concurrency import lockutils
       
    24 from oslo_config import cfg
       
    25 from oslo_db import api as oslo_db_api
       
    26 from oslo_log import log as logging
       
    27 from oslo_utils import importutils
       
    28 
       
    29 from neutron.api.rpc.agentnotifiers import dhcp_rpc_agent_api
       
    30 from neutron.api.rpc.agentnotifiers import l3_rpc_agent_api
       
    31 from neutron.api.rpc.handlers import dhcp_rpc
       
    32 from neutron.api.rpc.handlers import l3_rpc
       
    33 from neutron.api.rpc.handlers import metadata_rpc
       
    34 from neutron.api.v2 import attributes
       
    35 from neutron.common import constants
       
    36 from neutron.common import exceptions
       
    37 from neutron.common import rpc as n_rpc
       
    38 from neutron.common import topics
       
    39 from neutron.db import agents_db
       
    40 from neutron.db import agentschedulers_db
       
    41 from neutron.db import api as db
       
    42 from neutron.db import db_base_plugin_v2
       
    43 from neutron.db import external_net_db
       
    44 from neutron.db import l3_agentschedulers_db
       
    45 from neutron.db import l3_attrs_db
       
    46 from neutron.db import l3_gwmode_db
       
    47 from neutron.db import models_v2
       
    48 from neutron.db import portbindings_db
       
    49 from neutron.db import quota_db
       
    50 from neutron.db import securitygroups_db
       
    51 from neutron.extensions import external_net
       
    52 from neutron.extensions import providernet
       
    53 from neutron.plugins.common import constants as svc_constants
       
    54 from neutron.plugins.ml2 import models
       
    55 
       
    56 LOG = logging.getLogger(__name__)
       
    57 # Only import the vpn server code if it exists.
       
    58 try:
       
    59     sp = cfg.CONF.service_plugins
       
    60     vpns = 'vpnaas'
       
    61     if vpns in sp:
       
    62         try:
       
    63             from neutron_vpnaas.db.vpn import vpn_db
       
    64             LOG.debug("Loading VPNaaS service driver.")
       
    65         except ImportError:
       
    66             pass
       
    67     else:
       
    68         LOG.debug("vpnaas service_plugin not configured")
       
    69 except:
       
    70     pass
       
    71 
       
    72 evs_controller_opts = [
       
    73     cfg.StrOpt('evs_controller', default='ssh://evsuser@localhost',
       
    74                help=_("An URI that specifies an EVS controller"))
       
    75 ]
       
    76 
       
    77 cfg.CONF.register_opts(evs_controller_opts, "EVS")
       
    78 
       
    79 
       
    80 class EVSControllerError(exceptions.NeutronException):
       
    81     message = _("EVS controller: %(errmsg)s")
       
    82 
       
    83     def __init__(self, evs_errmsg):
       
    84         super(EVSControllerError, self).__init__(errmsg=evs_errmsg)
       
    85 
       
    86 
       
    87 class EVSOpNotSupported(exceptions.NeutronException):
       
    88     message = _("Operation not supported by EVS plugin: %(opname)s")
       
    89 
       
    90     def __init__(self, evs_errmsg):
       
    91         super(EVSOpNotSupported, self).__init__(opname=evs_errmsg)
       
    92 
       
    93 
       
    94 class EVSNotFound(exceptions.NeutronException):
       
    95     message = _("Network %(net_id)s could not be found in EVS")
       
    96 
       
    97 
       
    98 class EVSNeutronPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
       
    99                          agentschedulers_db.DhcpAgentSchedulerDbMixin,
       
   100                          external_net_db.External_net_db_mixin,
       
   101                          l3_agentschedulers_db.L3AgentSchedulerDbMixin,
       
   102                          l3_gwmode_db.L3_NAT_db_mixin):
       
   103     """Implements v2 Neutron Plug-in API specification.
       
   104 
       
   105     All the neutron API calls to create/delete/retrieve Network/Subnet/Port
       
   106     are forwarded to EVS controller through Solaris RAD. The RAD connection
       
   107     to EVS Controller is over SSH. In order that this plugin can communicate
       
   108     with EVS Controller non-interactively and securely, one should setup SSH
       
   109     authentication with pre-shared keys between the host running neutron-server
       
   110     and the host running EVS controller.
       
   111 
       
   112     The following table maps OpenStack Neutron resources and attributes to
       
   113     Solaris Elastic Virtual Switch resources and attributes
       
   114 
       
   115     |---------------------+------------------+------------------------------|
       
   116     | OpenStack Neutron   | Solaris EVS      | Comments                     |
       
   117     |---------------------+------------------+------------------------------|
       
   118     | Network             | EVS              | Represents an isolated L2    |
       
   119     | -- name             | -- name          | segment; implemented either  |
       
   120     | -- id               | -- uuid          | through VLANs or VXLANs      |
       
   121     | -- tenant_id        | -- tenant        |                              |
       
   122     | -- shared           | Always False     |                              |
       
   123     | -- admin_state_up   | Always True      |                              |
       
   124     | -- status           | Always ACTIVE    |                              |
       
   125     | -- provider:        |                  |                              |
       
   126     |    network_type     |  -- l2-type      | (either VLAN or VXLAN)       |
       
   127     | -- provider:        |                  |                              |
       
   128     |    segmentation_id  |  -- vlanid/vni   |                              |
       
   129     |                     |                  |                              |
       
   130     |                     |                  |                              |
       
   131     | Subnet              | IPnet            | An IP network represents     |
       
   132     | -- name             | -- name          | a block of either IPv4       |
       
   133     | -- id               | -- uuid          | or IPv6 addresses (subnet)   |
       
   134     | -- network_id       | -- evs           | along with a default router  |
       
   135     | -- tenant_id        | -- tenant        | for the block                |
       
   136     | -- cidr             | -- subnet        |                              |
       
   137     | -- gateway_ip       | -- defrouter     |                              |
       
   138     | -- allocation_pools | -- start/stop    |                              |
       
   139     | -- dns_nameservers  | -- OpenStack:\   |                              |
       
   140     |                     | dns_nameservers  |                              |
       
   141     | -- host_routes      | -- OpenStack:\   |                              |
       
   142     |                     | host_routes      |                              |
       
   143     | -- enable_dhcp      | -- OpenStack:\   |                              |
       
   144     |                     | enable_dhcp      |                              |
       
   145     | -- shared           | Always False     |                              |
       
   146     |                     |                  |                              |
       
   147     | Port                | VPort            | A VPort represents the point |
       
   148     | -- name             | -- name          | of attachment between the    |
       
   149     | -- id               | -- uuid          | VNIC and an EVS. It          |
       
   150     | -- network_id       | -- evs           | encapsulates various network |
       
   151     | -- tenant_id        | -- tenant        | configuration parameters (   |
       
   152     | -- status           | -- status        | MAC addresses, IP addresses, |
       
   153     | -- mac_address      | -- macaddr       | and SLAs)                    |
       
   154     | -- fixed_ips        | -- ipaddr        |                              |
       
   155     | -- device_id        | -- OpenStack:\   |                              |
       
   156     |                     |    device_id     |                              |
       
   157     | -- device_owner     | -- OpenStack:\   |                              |
       
   158     |                     |    device_owner  |                              |
       
   159     | -- security_groups  | -- Not Supported |                              |
       
   160     | -- admin_state_up   | Always UP        |                              |
       
   161     |---------------------+------------------+------------------------------|
       
   162     """
       
   163 
       
   164     _supported_extension_aliases = ["provider", "external-net", "router",
       
   165                                     "ext-gw-mode", "quotas", "agent",
       
   166                                     "l3_agent_scheduler",
       
   167                                     "dhcp_agent_scheduler"]
       
   168 
       
   169     def __init__(self):
       
   170         self.network_scheduler = importutils.import_object(
       
   171             cfg.CONF.network_scheduler_driver
       
   172         )
       
   173         self.router_scheduler = importutils.import_object(
       
   174             cfg.CONF.router_scheduler_driver
       
   175         )
       
   176         self._setup_rpc()
       
   177         self._rad_connection = None
       
   178 
       
   179     @property
       
   180     def rad_connection(self):
       
   181         # Since there is no connect_uri() yet, we need to do
       
   182         # parsing of ssh://user@hostname ourselves
       
   183         suh = cfg.CONF.EVS.evs_controller.split('://')
       
   184         if len(suh) != 2 or suh[0] != 'ssh' or not suh[1].strip():
       
   185             raise SystemExit(_("Specified evs_controller is invalid"))
       
   186         uh = suh[1].split('@')
       
   187         if len(uh) != 2 or not uh[0].strip() or not uh[1].strip():
       
   188             raise SystemExit(_("'user' and 'hostname' need to be specified "
       
   189                                "for evs_controller"))
       
   190 
       
   191         if (self._rad_connection is not None and
       
   192                 self._rad_connection._closed is None):
       
   193             return self._rad_connection
       
   194 
       
   195         LOG.debug(_("Connecting to EVS Controller at %s as %s") %
       
   196                   (uh[1], uh[0]))
       
   197         self._rad_connection = radcon.connect_ssh(uh[1], user=uh[0])
       
   198         return self._rad_connection
       
   199 
       
   200     def _setup_rpc(self):
       
   201         # RPC support
       
   202         self.service_topics = {svc_constants.CORE: topics.PLUGIN,
       
   203                                svc_constants.L3_ROUTER_NAT: topics.L3PLUGIN}
       
   204         self.conn = n_rpc.create_connection(new=True)
       
   205         self.endpoints = [dhcp_rpc.DhcpRpcCallback(),
       
   206                           l3_rpc.L3RpcCallback(),
       
   207                           agents_db.AgentExtRpcCallback(),
       
   208                           metadata_rpc.MetadataRpcCallback()]
       
   209         for svc_topic in self.service_topics.values():
       
   210             self.conn.create_consumer(svc_topic, self.endpoints, fanout=False)
       
   211         # Consume from all consumers in a thread
       
   212         self.conn.consume_in_threads()
       
   213         self.dhcp_agent_notifier = dhcp_rpc_agent_api.DhcpAgentNotifyAPI()
       
   214         self.l3_agent_notifier = l3_rpc_agent_api.L3AgentNotifyAPI()
       
   215 
       
   216         # needed by AgentSchedulerDbMixin()
       
   217         self.agent_notifiers[constants.AGENT_TYPE_DHCP] = \
       
   218             self.dhcp_agent_notifier
       
   219         self.agent_notifiers[constants.AGENT_TYPE_L3] = \
       
   220             self.l3_agent_notifier
       
   221 
       
   222     @property
       
   223     def supported_extension_aliases(self):
       
   224         return self._supported_extension_aliases
       
   225 
       
   226     @lockutils.synchronized('evs-plugin', 'neutron-')
       
   227     def _evs_controller_addIPnet(self, tenantname, evsname, ipnetname,
       
   228                                  propstr):
       
   229         LOG.debug(_("Adding IPnet: %s with properties: %s for tenant: %s "
       
   230                     "and for evs: %s") %
       
   231                   (ipnetname, propstr, tenantname, evsname))
       
   232 
       
   233         pat = radcli.ADRGlobPattern({'name': evsname,
       
   234                                      'tenant': tenantname})
       
   235         try:
       
   236             evs = self.rad_connection.get_object(evsbind.EVS(), pat)
       
   237             ipnet = evs.addIPnet(propstr, ipnetname)
       
   238         except radcli.ObjectError as oe:
       
   239             raise EVSControllerError(oe.get_payload().errmsg)
       
   240         return ipnet
       
   241 
       
   242     @lockutils.synchronized('evs-plugin', 'neutron-')
       
   243     def _evs_controller_updateIPnet(self, ipnetuuid, propstr):
       
   244         LOG.debug(_("Updating IPnet with id: %s with property string: %s") %
       
   245                   (ipnetuuid, propstr))
       
   246         pat = radcli.ADRGlobPattern({'uuid': ipnetuuid})
       
   247         try:
       
   248             ipnetlist = self.rad_connection.list_objects(evsbind.IPnet(), pat)
       
   249             if not ipnetlist:
       
   250                 return
       
   251             assert len(ipnetlist) == 1
       
   252             ipnet = self.rad_connection.get_object(ipnetlist[0])
       
   253             ipnet.setProperty(propstr)
       
   254         except radcli.ObjectError as oe:
       
   255             raise EVSControllerError(oe.get_payload().errmsg)
       
   256 
       
   257     def _subnet_pool_to_evs_pool(self, subnet):
       
   258         poolstr = ""
       
   259         # obtain the optional allocation pool
       
   260         pools = subnet.get('allocation_pools')
       
   261         if not pools or pools is attributes.ATTR_NOT_SPECIFIED:
       
   262             return poolstr
       
   263 
       
   264         for pool in pools:
       
   265             if poolstr:
       
   266                 poolstr += ","
       
   267             # if start and end address is same, EVS expects the address
       
   268             # to be provided as-is instead of x.x.x.x-x.x.x.x
       
   269             if pool['start'] == pool['end']:
       
   270                 poolstr += pool['start']
       
   271             else:
       
   272                 poolstr += "%s-%s" % (pool['start'], pool['end'])
       
   273         return poolstr
       
   274 
       
   275     def create_subnet(self, context, subnet):
       
   276         """Creates a subnet(IPnet) for a given network(EVS).
       
   277 
       
   278          An IP network represents a block of either IPv4 or IPv6 addresses
       
   279          (i.e., subnet) along with a default router for the block. Only one
       
   280          IPnet can be associated with an EVS. All the zones/VNICs that
       
   281          connect to the EVS, through a VPort, will get an IP address from the
       
   282          IPnet associated with the EVS.
       
   283         """
       
   284 
       
   285         if (subnet['subnet']['host_routes'] is not
       
   286                 attributes.ATTR_NOT_SPECIFIED):
       
   287             raise EVSOpNotSupported(_("setting --host-route for a subnet "
       
   288                                       "not supported"))
       
   289 
       
   290         poolstr = self._subnet_pool_to_evs_pool(subnet['subnet'])
       
   291 
       
   292         with context.session.begin(subtransactions=True):
       
   293             # create the subnet in the DB
       
   294             db_subnet = super(EVSNeutronPluginV2, self).create_subnet(context,
       
   295                                                                       subnet)
       
   296             ipnetname = db_subnet['name']
       
   297             if not ipnetname:
       
   298                 ipnetname = None
       
   299             evsname = db_subnet['network_id']
       
   300             tenantname = db_subnet['tenant_id']
       
   301             proplist = ['subnet=%s' % db_subnet['cidr']]
       
   302             defrouter = db_subnet['gateway_ip']
       
   303             if not defrouter:
       
   304                 defrouter = '0.0.0.0' if db_subnet['ip_version'] == 4 else '::'
       
   305             proplist.append('defrouter=%s' % defrouter)
       
   306             proplist.append('uuid=%s' % db_subnet['id'])
       
   307             if poolstr:
       
   308                 proplist.append('pool=%s' % (poolstr))
       
   309             self._evs_controller_addIPnet(tenantname, evsname, ipnetname,
       
   310                                           ",".join(proplist))
       
   311 
       
   312         return db_subnet
       
   313 
       
   314     def update_subnet(self, context, id, subnet):
       
   315         LOG.debug(_("Updating Subnet: %s with %s") % (id, subnet))
       
   316         if (set(subnet['subnet'].keys()) - set(('enable_dhcp',
       
   317                                                 'allocation_pools',
       
   318                                                 'dns_nameservers',
       
   319                                                 'ipv6_address_mode',
       
   320                                                 'ipv6_ra_mode'))):
       
   321                 raise EVSOpNotSupported(_("only following subnet attributes "
       
   322                                           "enable-dhcp, allocation-pool, "
       
   323                                           "dns-nameserver, ipv6-address-mode, "
       
   324                                           "and ipv6-ra-mode can be updated"))
       
   325 
       
   326         poolstr = self._subnet_pool_to_evs_pool(subnet['subnet'])
       
   327 
       
   328         with context.session.begin(subtransactions=True):
       
   329             # update subnet in DB
       
   330             retval = super(EVSNeutronPluginV2, self).\
       
   331                 update_subnet(context, id, subnet)
       
   332             # update EVS IPnet with allocation pool info
       
   333             if poolstr:
       
   334                 self._evs_controller_updateIPnet(id, "pool=%s" % poolstr)
       
   335 
       
   336         return retval
       
   337 
       
   338     def get_subnet(self, context, id, fields=None):
       
   339         LOG.debug(_("Getting subnet: %s"), id)
       
   340         subnet = super(EVSNeutronPluginV2, self).get_subnet(context, id, None)
       
   341         return self._fields(subnet, fields)
       
   342 
       
   343     def get_subnets(self, context, filters=None, fields=None,
       
   344                     sorts=None, limit=None, marker=None, page_reverse=False):
       
   345         subnets = super(EVSNeutronPluginV2, self).\
       
   346             get_subnets(context, filters, None, sorts, limit, marker,
       
   347                         page_reverse)
       
   348         return [self._fields(subnet, fields) for subnet in subnets]
       
   349 
       
   350     @lockutils.synchronized('evs-plugin', 'neutron-')
       
   351     def _evs_controller_removeIPnet(self, tenantname, evsname, ipnetuuid,
       
   352                                     auto_created_ports):
       
   353         LOG.debug(_("Removing IPnet with id: %s for tenant: %s for evs: %s") %
       
   354                   (ipnetuuid, tenantname, evsname))
       
   355         pat = radcli.ADRGlobPattern({'name': evsname, 'tenant': tenantname})
       
   356         try:
       
   357             evs = self.rad_connection.get_object(evsbind.EVS(), pat)
       
   358             if auto_created_ports:
       
   359                 LOG.debug(_("Need to remove following ports %s before "
       
   360                             "removing the IPnet") % (auto_created_ports))
       
   361                 for port in auto_created_ports:
       
   362                     try:
       
   363                         evs.removeVPort(port['id'], "force=yes")
       
   364                     except radcli.ObjectError as oe:
       
   365                         # '43' corresponds to EVS' EVS_ENOENT_VPORT error code
       
   366                         if oe.get_payload().err == 43:
       
   367                             LOG.debug(_("VPort %s could not be found") %
       
   368                                       (port['id']))
       
   369             evs.removeIPnet(ipnetuuid)
       
   370         except (radcli.NotFoundError, radcli.ObjectError) as oe:
       
   371             # '42' corresponds to EVS' EVS_ENOENT_IPNET error code
       
   372             if oe.get_payload() is None or oe.get_payload().err == 42:
       
   373                 # EVS doesn't have that IPnet, return success to delete
       
   374                 # the IPnet from Neutron DB.
       
   375                 LOG.debug(_("IPnet could not be found in EVS."))
       
   376                 return
       
   377             raise EVSControllerError(oe.get_payload().errmsg)
       
   378 
       
   379     def delete_subnet(self, context, id):
       
   380         subnet = self.get_subnet(context, id)
       
   381         if not subnet:
       
   382             return
       
   383 
       
   384         with context.session.begin(subtransactions=True):
       
   385             # get a list of ports automatically created by Neutron
       
   386             auto_created_ports = context.session.query(models_v2.Port).\
       
   387                 filter(models_v2.Port.device_owner.
       
   388                        in_(db_base_plugin_v2.AUTO_DELETE_PORT_OWNERS)).all()
       
   389             # delete subnet in DB
       
   390             super(EVSNeutronPluginV2, self).delete_subnet(context, id)
       
   391             self._evs_controller_removeIPnet(subnet['tenant_id'],
       
   392                                              subnet['network_id'], id,
       
   393                                              auto_created_ports)
       
   394 
       
   395     @lockutils.synchronized('evs-plugin', 'neutron-')
       
   396     def _evs_controller_createEVS(self, tenantname, evsname, propstr):
       
   397         LOG.debug(_("Adding EVS: %s with properties: %s for tenant: %s") %
       
   398                   (evsname, propstr, tenantname))
       
   399         try:
       
   400             evs = self.rad_connection.\
       
   401                 get_object(evsbind.EVSController()).\
       
   402                 createEVS(propstr, tenantname, evsname)
       
   403         except radcli.ObjectError as oe:
       
   404             raise EVSControllerError(oe.get_payload().errmsg)
       
   405         return evs
       
   406 
       
   407     def _extend_network_dict(self, network, evs):
       
   408         for prop in evs.props:
       
   409             if prop.name == 'l2-type':
       
   410                 network[providernet.NETWORK_TYPE] = prop.value
       
   411             elif prop.name == 'vlanid' or prop.name == 'vni':
       
   412                 network[providernet.SEGMENTATION_ID] = int(prop.value)
       
   413 
       
   414     def create_network(self, context, network):
       
   415         """Creates a network(EVS) for a given tenant.
       
   416 
       
   417         An Elastic Virtual Switch (EVS) is a virtual switch that spans
       
   418         one or more servers (physical machines). It represents an isolated L2
       
   419         segment, and the isolation is implemented either through VLANs or
       
   420         VXLANs. An EVS provides network connectivity between the Virtual
       
   421         Machines connected to it. There are two main resources associated with
       
   422         an EVS: IPnet and VPort.
       
   423         """
       
   424 
       
   425         if network['network']['admin_state_up'] is False:
       
   426             raise EVSOpNotSupported(_("setting admin_state_up=False for a "
       
   427                                       "network not supported"))
       
   428 
       
   429         if network['network']['shared'] is True:
       
   430             raise EVSOpNotSupported(_("setting shared=True for a "
       
   431                                       "network not supported"))
       
   432 
       
   433         evsname = network['network']['name']
       
   434         if not evsname:
       
   435             evsname = None
       
   436 
       
   437         tenantname = self._get_tenant_id_for_create(context,
       
   438                                                     network['network'])
       
   439         proplist = []
       
   440         network_type = network['network'][providernet.NETWORK_TYPE]
       
   441         if attributes.is_attr_set(network_type):
       
   442             proplist.append('l2-type=%s' % network_type)
       
   443 
       
   444         segment_id = network['network'][providernet.SEGMENTATION_ID]
       
   445         if attributes.is_attr_set(segment_id):
       
   446             if (not attributes.is_attr_set(network_type) or
       
   447                     len(network_type) == 0):
       
   448                 raise EVSControllerError(_("provider:network_type must be "
       
   449                                            "specified when provider:"
       
   450                                            "segmentation_id is provided"))
       
   451 
       
   452             if network_type == 'vxlan':
       
   453                 proplist.append('vni=%d' % segment_id)
       
   454             elif network_type == 'vlan':
       
   455                 proplist.append('vlanid=%d' % segment_id)
       
   456             else:
       
   457                 raise EVSControllerError(_("specified "
       
   458                                            "provider:network_type '%s' not "
       
   459                                            "supported") % network_type)
       
   460 
       
   461         propstr = None
       
   462         if proplist:
       
   463             propstr = ",".join(proplist)
       
   464 
       
   465         with context.session.begin(subtransactions=True):
       
   466             # create the network in DB
       
   467             net = super(EVSNeutronPluginV2, self).create_network(context,
       
   468                                                                  network)
       
   469             self._process_l3_create(context, net, network['network'])
       
   470             # if --router:external is not set, the above function does
       
   471             # not update net with router:external set to False
       
   472             if net.get(external_net.EXTERNAL) is None:
       
   473                 net[external_net.EXTERNAL] = False
       
   474 
       
   475             # create EVS on the EVS controller
       
   476             if propstr:
       
   477                 propstr += ",uuid=%s" % net['id']
       
   478             else:
       
   479                 propstr = "uuid=%s" % net['id']
       
   480             evs = self._evs_controller_createEVS(tenantname, evsname, propstr)
       
   481 
       
   482             # add provider information into net
       
   483             self._extend_network_dict(net, evs)
       
   484 
       
   485         return net
       
   486 
       
   487     def update_network(self, context, id, network):
       
   488         raise EVSOpNotSupported(_("net-update"))
       
   489 
       
   490     @lockutils.synchronized('evs-plugin', 'neutron-')
       
   491     def _evs_controller_getEVS(self, evsuuid):
       
   492         LOG.debug(_("Getting EVS: %s"), evsuuid)
       
   493         try:
       
   494             evslist = self.rad_connection.\
       
   495                 get_object(evsbind.EVSController()).\
       
   496                 getEVSInfo('evs=%s' % evsuuid)
       
   497         except radcli.ObjectError as oe:
       
   498             raise EVSControllerError(oe.getpayload().errmsg)
       
   499         if not evslist:
       
   500             LOG.error(_("EVS framework does not have Neutron network "
       
   501                         "'%s' defined"), evsuuid)
       
   502             return None
       
   503         return evslist[0]
       
   504 
       
   505     def get_network(self, context, id, fields=None):
       
   506         with context.session.begin(subtransactions=True):
       
   507             net = super(EVSNeutronPluginV2, self).get_network(context,
       
   508                                                               id, None)
       
   509             # call EVS controller to get provider network information
       
   510             evs = self._evs_controller_getEVS(net['id'])
       
   511             if evs:
       
   512                 self._extend_network_dict(net, evs)
       
   513         return self._fields(net, fields)
       
   514 
       
   515     def get_networks(self, context, filters=None, fields=None,
       
   516                      sorts=None, limit=None, marker=None, page_reverse=False):
       
   517 
       
   518         with context.session.begin(subtransactions=True):
       
   519             nets = super(EVSNeutronPluginV2, self).\
       
   520                 get_networks(context, filters, None, sorts, limit, marker,
       
   521                              page_reverse)
       
   522             for net in nets:
       
   523                 evs = self._evs_controller_getEVS(net['id'])
       
   524                 if evs:
       
   525                     self._extend_network_dict(net, evs)
       
   526         return [self._fields(net, fields) for net in nets]
       
   527 
       
   528     @lockutils.synchronized('evs-plugin', 'neutron-')
       
   529     def _evs_controller_deleteEVS(self, tenantname, evsuuid):
       
   530         LOG.debug(_("Removing EVS with id: %s for tenant: %s") %
       
   531                   (evsuuid, tenantname))
       
   532         try:
       
   533             self.rad_connection.\
       
   534                 get_object(evsbind.EVSController()).\
       
   535                 deleteEVS(evsuuid, tenantname)
       
   536         except (radcli.NotFoundError, radcli.ObjectError) as oe:
       
   537             # '41' corresponds to EVS' EVS_ENOENT_EVS error code
       
   538             if oe.get_payload() is None or oe.get_payload().err == 41:
       
   539                 # EVS doesn't have that EVS, return success to delete
       
   540                 # the EVS from Neutron DB.
       
   541                 LOG.debug(_("EVS could not be found in EVS backend."))
       
   542                 return
       
   543             raise EVSControllerError(oe.get_payload().errmsg)
       
   544 
       
   545     def delete_network(self, context, id):
       
   546         with context.session.begin(subtransactions=True):
       
   547             network = self._get_network(context, id)
       
   548 
       
   549             qry_network_ports = context.session.query(models_v2.Port).\
       
   550                 filter_by(network_id=id).filter(models_v2.Port.device_owner.
       
   551                                                 in_(db_base_plugin_v2.
       
   552                                                     AUTO_DELETE_PORT_OWNERS))
       
   553 
       
   554             auto_created_ports = qry_network_ports.all()
       
   555             qry_network_ports.delete(synchronize_session=False)
       
   556 
       
   557             port_in_use = context.session.query(models_v2.Port).filter_by(
       
   558                 network_id=id).first()
       
   559 
       
   560             if port_in_use:
       
   561                 raise exceptions.NetworkInUse(net_id=id)
       
   562 
       
   563             # clean up subnets
       
   564             subnets = self._get_subnets_by_network(context, id)
       
   565             for subnet in subnets:
       
   566                 super(EVSNeutronPluginV2, self).delete_subnet(context,
       
   567                                                               subnet['id'])
       
   568                 self._evs_controller_removeIPnet(subnet['tenant_id'],
       
   569                                                  subnet['network_id'],
       
   570                                                  subnet['id'],
       
   571                                                  auto_created_ports)
       
   572 
       
   573             context.session.delete(network)
       
   574             self._evs_controller_deleteEVS(network['tenant_id'], id)
       
   575 
       
   576     @lockutils.synchronized('evs-plugin', 'neutron-')
       
   577     def _evs_controller_addVPort(self, tenantname, evsname, vportname,
       
   578                                  propstr):
       
   579         LOG.debug(_("Adding VPort: %s with properties: %s for tenant: %s "
       
   580                     "and for evs: %s") %
       
   581                   (vportname, propstr, tenantname, evsname))
       
   582 
       
   583         try:
       
   584             pat = radcli.ADRGlobPattern({'name': evsname,
       
   585                                          'tenant': tenantname})
       
   586             evs = self.rad_connection.get_object(evsbind.EVS(), pat)
       
   587             vport = evs.addVPort(propstr, vportname)
       
   588         except radcli.ObjectError as oe:
       
   589             raise EVSControllerError(oe.get_payload().errmsg)
       
   590         return vport
       
   591 
       
   592     @oslo_db_api.wrap_db_retry(max_retries=db.MAX_RETRIES,
       
   593                                retry_on_request=True,
       
   594                                retry_on_deadlock=True)
       
   595     def create_port(self, context, port):
       
   596         """Creates a port(VPort) for a given network(EVS).
       
   597 
       
   598          A VPort represents the point of attachment between the VNIC and an
       
   599          EVS. It encapsulates various network configuration parameters such as
       
   600              -- SLAs (maxbw, cos, and priority)
       
   601              -- IP address and
       
   602              -- MAC address, et al
       
   603          This configuration is inherited by the VNIC when it connects to the
       
   604          VPort.
       
   605         """
       
   606         if port['port']['admin_state_up'] is False:
       
   607             raise EVSOpNotSupported(_("setting admin_state_up=False for a "
       
   608                                       "port not supported"))
       
   609 
       
   610         with context.session.begin(subtransactions=True):
       
   611             # for external gateway ports and floating ips, tenant_id
       
   612             # is not set, but EVS does not like it.
       
   613             tenant_id = self._get_tenant_id_for_create(context, port['port'])
       
   614             if not tenant_id:
       
   615                 network = self._get_network(context,
       
   616                                             port['port']['network_id'])
       
   617                 port['port']['tenant_id'] = network['tenant_id']
       
   618             # create the port in the DB
       
   619             db_port = super(EVSNeutronPluginV2, self).create_port(context,
       
   620                                                                   port)
       
   621             # Neutron allows to create a port on a network that doesn't
       
   622             # yet have subnet associated with it, however EVS doesn't
       
   623             # support this.
       
   624             if not db_port['fixed_ips']:
       
   625                 raise EVSOpNotSupported(_("creating a port on a network that "
       
   626                                           "does not yet have subnet "
       
   627                                           "associated with it is not "
       
   628                                           "supported"))
       
   629             tenantname = db_port['tenant_id']
       
   630             vportname = db_port['name']
       
   631             if not vportname:
       
   632                 vportname = None
       
   633             evs_id = db_port['network_id']
       
   634             proplist = ['macaddr=%s' % db_port['mac_address']]
       
   635             proplist.append('ipaddr=%s' %
       
   636                             db_port['fixed_ips'][0].get('ip_address'))
       
   637             proplist.append('uuid=%s' % db_port['id'])
       
   638 
       
   639             self._evs_controller_addVPort(tenantname, evs_id, vportname,
       
   640                                           ",".join(proplist))
       
   641         return db_port
       
   642 
       
   643     def update_port(self, context, id, port):
       
   644         # EVS does not allow updating certain attributes, so check for it
       
   645         state = port['port'].get('admin_state_up')
       
   646         if state and state is False:
       
   647             raise EVSOpNotSupported(_("updating port's admin_state_up to "
       
   648                                       "False is not supported"))
       
   649 
       
   650         # Get the original port and fail if any attempt is being made
       
   651         # to change fixed_ips of the port since EVS doesn't support it
       
   652         original_port = super(EVSNeutronPluginV2, self).get_port(context, id)
       
   653         original_ips = original_port['fixed_ips']
       
   654         update_ips = port['port'].get('fixed_ips')
       
   655         if (update_ips and
       
   656             (len(update_ips) != 1 or
       
   657              update_ips[0]['subnet_id'] != original_ips[0]['subnet_id'] or
       
   658              update_ips[0]['ip_address'] != original_ips[0]['ip_address'])):
       
   659             raise EVSOpNotSupported(_("updating port's fixed_ips "
       
   660                                       "is not supported"))
       
   661         LOG.debug(_("Updating port %s with %s") % (id, port))
       
   662         db_port = super(EVSNeutronPluginV2, self).update_port(context,
       
   663                                                               id, port)
       
   664         return db_port
       
   665 
       
   666     def get_port(self, context, id, fields=None):
       
   667         LOG.debug(_("Getting port: %s"), id)
       
   668         port = super(EVSNeutronPluginV2, self).get_port(context, id, None)
       
   669         return self._fields(port, fields)
       
   670 
       
   671     def get_ports(self, context, filters=None, fields=None,
       
   672                   sorts=None, limit=None, marker=None, page_reverse=False):
       
   673         ports = super(EVSNeutronPluginV2, self).\
       
   674             get_ports(context, filters, None, sorts, limit, marker,
       
   675                       page_reverse)
       
   676         return [self._fields(port, fields) for port in ports]
       
   677 
       
   678     @lockutils.synchronized('evs-plugin', 'neutron-')
       
   679     def _evs_controller_removeVPort(self, tenantname, evsname, vportuuid):
       
   680         LOG.debug(_("Removing VPort with id: %s for tenant: %s for evs: %s") %
       
   681                   (vportuuid, tenantname, evsname))
       
   682         pat = radcli.ADRGlobPattern({'name': evsname,
       
   683                                      'tenant': tenantname})
       
   684         try:
       
   685             evs = self.rad_connection.get_object(evsbind.EVS(), pat)
       
   686             evs.removeVPort(vportuuid, "force=yes")
       
   687         except (radcli.NotFoundError, radcli.ObjectError) as oe:
       
   688             # '43' corresponds to EVS' EVS_ENOENT_VPORT error code
       
   689             if oe.get_payload() is None or oe.get_payload().err == 43:
       
   690                 # EVS doesn't have that VPort, return success to delete
       
   691                 # the VPort from Neutron DB.
       
   692                 LOG.debug(_("VPort could not be found in EVS."))
       
   693             else:
       
   694                 raise EVSControllerError(oe.get_payload().errmsg)
       
   695 
       
   696     def delete_port(self, context, id, l3_port_check=True):
       
   697         if l3_port_check:
       
   698             self.prevent_l3_port_deletion(context, id)
       
   699         self.disassociate_floatingips(context, id)
       
   700         port = self.get_port(context, id)
       
   701         if not port:
       
   702             return
       
   703         with context.session.begin(subtransactions=True):
       
   704             super(EVSNeutronPluginV2, self).delete_port(context, id)
       
   705             self._evs_controller_removeVPort(port['tenant_id'],
       
   706                                              port['network_id'],
       
   707                                              port['id'])