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 |
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. |
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 |
|