# Copyright 2013 Embrane, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from heleosapi import backend_operations as h_op from heleosapi import constants as h_con from heleosapi import exceptions as h_exc from oslo_config import cfg from oslo_log import log as logging from sqlalchemy.orm import exc from neutron.common import constants as l3_constants from neutron.common import exceptions as neutron_exc from neutron.db import extraroute_db from neutron.db import l3_db from neutron.db import models_v2 from neutron.extensions import l3 from neutron.i18n import _LE from neutron.plugins.embrane.agent import dispatcher from neutron.plugins.embrane.common import config # noqa from neutron.plugins.embrane.common import constants as p_con from neutron.plugins.embrane.common import contexts as embrane_ctx from neutron.plugins.embrane.common import operation from neutron.plugins.embrane.common import utils LOG = logging.getLogger(__name__) conf = cfg.CONF.heleos class EmbranePlugin(object): """Embrane Neutron plugin. uses the heleos(c) platform and a support L2 plugin to leverage networking in cloud environments. """ _l3super = extraroute_db.ExtraRoute_db_mixin def __init__(self): pass def _run_embrane_config(self): # read configurations config_esm_mgmt = conf.esm_mgmt config_admin_username = conf.admin_username config_admin_password = conf.admin_password config_router_image_id = conf.router_image config_security_zones = {h_con.SzType.IB: conf.inband_id, h_con.SzType.OOB: conf.oob_id, h_con.SzType.MGMT: conf.mgmt_id, h_con.SzType.DUMMY: conf.dummy_utif_id} config_resource_pool = conf.resource_pool_id self._embrane_async = conf.async_requests self._esm_api = h_op.BackendOperations( esm_mgmt=config_esm_mgmt, admin_username=config_admin_username, admin_password=config_admin_password, router_image_id=config_router_image_id, security_zones=config_security_zones, resource_pool=config_resource_pool) self._dispatcher = dispatcher.Dispatcher(self, self._embrane_async) def _make_router_dict(self, *args, **kwargs): return self._l3super._make_router_dict(self, *args, **kwargs) def _delete_router(self, context, router_id): self._l3super.delete_router(self, context, router_id) def _update_db_router_state(self, context, neutron_router, dva_state): if not dva_state: new_state = p_con.Status.ERROR elif dva_state == h_con.DvaState.POWER_ON: new_state = p_con.Status.ACTIVE else: new_state = p_con.Status.READY self._set_db_router_state(context, neutron_router, new_state) return new_state def _set_db_router_state(self, context, neutron_router, new_state): return utils.set_db_item_state(context, neutron_router, new_state) def _update_db_interfaces_state(self, context, neutron_router): router_ports = self.get_ports(context, {"device_id": [neutron_router["id"]]}) self._esm_api.update_ports_status(neutron_router["id"], router_ports) for port in router_ports: db_port = self._get_port(context, port["id"]) db_port["status"] = port["status"] context.session.merge(db_port) def _update_neutron_state(self, context, neutron_router, state): try: self._update_db_interfaces_state(context, neutron_router) except Exception: LOG.exception(_LE("Unhandled exception occurred")) return self._set_db_router_state(context, neutron_router, state) def _retrieve_prefix_from_port(self, context, neutron_port): subnet_id = neutron_port["fixed_ips"][0]["subnet_id"] subnet = utils.retrieve_subnet(context, subnet_id) prefix = subnet["cidr"].split("/")[1] return prefix # L3 extension def create_router(self, context, router): r = router["router"] self._get_tenant_id_for_create(context, r) db_router = self._l3super.create_router(self, context, router) neutron_router = self._get_router(context, db_router['id']) gw_port = neutron_router.gw_port # For now, only small flavor is used utif_info = (self._plugin_support.retrieve_utif_info(context, gw_port) if gw_port else None) ip_allocation_info = (utils.retrieve_ip_allocation_info(context, gw_port) if gw_port else None) neutron_router = self._l3super._get_router(self, context, neutron_router["id"]) neutron_router["status"] = p_con.Status.CREATING self._dispatcher.dispatch_l3( d_context=embrane_ctx.DispatcherContext( p_con.Events.CREATE_ROUTER, neutron_router, context, None), args=(h_con.Flavor.SMALL, utif_info, ip_allocation_info)) return self._make_router_dict(neutron_router) def update_router(self, context, id, router): db_router = self._l3super.update_router(self, context, id, router) neutron_router = self._get_router(context, db_router['id']) gw_port = neutron_router.gw_port utif_info = (self._plugin_support.retrieve_utif_info(context, gw_port) if gw_port else None) ip_allocation_info = (utils.retrieve_ip_allocation_info(context, gw_port) if gw_port else None) routes_info = router["router"].get("routes") neutron_router = self._l3super._get_router(self, context, id) state_change = operation.Operation( self._set_db_router_state, args=(context, neutron_router, p_con.Status.UPDATING)) self._dispatcher.dispatch_l3( d_context=embrane_ctx.DispatcherContext( p_con.Events.UPDATE_ROUTER, neutron_router, context, state_change), args=(utif_info, ip_allocation_info, routes_info)) return self._make_router_dict(neutron_router) def get_router(self, context, id, fields=None): """Ensures that id does exist in the ESM.""" neutron_router = self._get_router(context, id) try: if neutron_router["status"] != p_con.Status.CREATING: self._esm_api.get_dva(id) except h_exc.DvaNotFound: LOG.error(_LE("The following routers have not physical match: %s"), id) self._set_db_router_state(context, neutron_router, p_con.Status.ERROR) LOG.debug("Requested router: %s", neutron_router) return self._make_router_dict(neutron_router, fields) def get_routers(self, context, filters=None, fields=None, sorts=None, limit=None, marker=None, page_reverse=False): """Retrieves the router list defined by the incoming filters.""" router_query = self._apply_filters_to_query( self._model_query(context, l3_db.Router), l3_db.Router, filters) id_list = [x["id"] for x in router_query if x["status"] != p_con.Status.CREATING] try: self._esm_api.get_dvas(id_list) except h_exc.DvaNotFound: LOG.error(_LE("The following routers have not physical match: %s"), repr(id_list)) error_routers = [] for id in id_list: try: error_routers.append(self._get_router(context, id)) except l3.RouterNotFound: pass for error_router in error_routers: self._set_db_router_state(context, error_router, p_con.Status.ERROR) return [self._make_router_dict(router, fields) for router in router_query] def delete_router(self, context, id): """Deletes the DVA with the specific router id.""" # Copy of the parent validation code, shouldn't the base modules # provide functions for validating operations? device_owner_router_intf = l3_constants.DEVICE_OWNER_ROUTER_INTF fips = self.get_floatingips_count(context.elevated(), filters={"router_id": [id]}) if fips: raise l3.RouterInUse(router_id=id) device_filter = {"device_id": [id], "device_owner": [device_owner_router_intf]} ports = self.get_ports_count(context.elevated(), filters=device_filter) if ports: raise l3.RouterInUse(router_id=id) neutron_router = self._get_router(context, id) state_change = operation.Operation(self._set_db_router_state, args=(context, neutron_router, p_con.Status.DELETING)) self._dispatcher.dispatch_l3( d_context=embrane_ctx.DispatcherContext( p_con.Events.DELETE_ROUTER, neutron_router, context, state_change), args=()) LOG.debug("Deleting router=%s", neutron_router) return neutron_router def add_router_interface(self, context, router_id, interface_info): """Grows DVA interface in the specified subnet.""" neutron_router = self._get_router(context, router_id) rport_qry = context.session.query(models_v2.Port) ports = rport_qry.filter_by( device_id=router_id).all() if len(ports) >= p_con.UTIF_LIMIT: raise neutron_exc.BadRequest( resource=router_id, msg=("this router doesn't support more than " + str(p_con.UTIF_LIMIT) + " interfaces")) neutron_router_iface = self._l3super.add_router_interface( self, context, router_id, interface_info) port = self._get_port(context, neutron_router_iface["port_id"]) utif_info = self._plugin_support.retrieve_utif_info(context, port) ip_allocation_info = utils.retrieve_ip_allocation_info(context, port) state_change = operation.Operation(self._set_db_router_state, args=(context, neutron_router, p_con.Status.UPDATING)) self._dispatcher.dispatch_l3( d_context=embrane_ctx.DispatcherContext( p_con.Events.GROW_ROUTER_IF, neutron_router, context, state_change), args=(utif_info, ip_allocation_info)) return neutron_router_iface def remove_router_interface(self, context, router_id, interface_info): port_id = None if "port_id" in interface_info: port_id = interface_info["port_id"] elif "subnet_id" in interface_info: subnet_id = interface_info["subnet_id"] subnet = utils.retrieve_subnet(context, subnet_id) rport_qry = context.session.query(models_v2.Port) ports = rport_qry.filter_by( device_id=router_id, device_owner=l3_constants.DEVICE_OWNER_ROUTER_INTF, network_id=subnet["network_id"]) for p in ports: if p["fixed_ips"][0]["subnet_id"] == subnet_id: port_id = p["id"] break neutron_router = self._get_router(context, router_id) self._l3super.remove_router_interface(self, context, router_id, interface_info) state_change = operation.Operation(self._set_db_router_state, args=(context, neutron_router, p_con.Status.UPDATING)) self._dispatcher.dispatch_l3( d_context=embrane_ctx.DispatcherContext( p_con.Events.SHRINK_ROUTER_IF, neutron_router, context, state_change), args=(port_id,)) def create_floatingip(self, context, floatingip): result = self._l3super.create_floatingip( self, context, floatingip) if result["port_id"]: neutron_router = self._get_router(context, result["router_id"]) db_fixed_port = self._get_port(context, result["port_id"]) fixed_prefix = self._retrieve_prefix_from_port(context, db_fixed_port) db_floating_port = neutron_router["gw_port"] floating_prefix = self._retrieve_prefix_from_port( context, db_floating_port) nat_info = utils.retrieve_nat_info(context, result, fixed_prefix, floating_prefix, neutron_router) state_change = operation.Operation( self._set_db_router_state, args=(context, neutron_router, p_con.Status.UPDATING)) self._dispatcher.dispatch_l3( d_context=embrane_ctx.DispatcherContext( p_con.Events.SET_NAT_RULE, neutron_router, context, state_change), args=(nat_info,)) return result def update_floatingip(self, context, id, floatingip): db_fip = self._l3super.get_floatingip(self, context, id) result = self._l3super.update_floatingip(self, context, id, floatingip) if db_fip["port_id"] and db_fip["port_id"] != result["port_id"]: neutron_router = self._get_router(context, db_fip["router_id"]) fip_id = db_fip["id"] state_change = operation.Operation( self._set_db_router_state, args=(context, neutron_router, p_con.Status.UPDATING)) self._dispatcher.dispatch_l3( d_context=embrane_ctx.DispatcherContext( p_con.Events.RESET_NAT_RULE, neutron_router, context, state_change), args=(fip_id,)) if result["port_id"]: neutron_router = self._get_router(context, result["router_id"]) db_fixed_port = self._get_port(context, result["port_id"]) fixed_prefix = self._retrieve_prefix_from_port(context, db_fixed_port) db_floating_port = neutron_router["gw_port"] floating_prefix = self._retrieve_prefix_from_port( context, db_floating_port) nat_info = utils.retrieve_nat_info(context, result, fixed_prefix, floating_prefix, neutron_router) state_change = operation.Operation( self._set_db_router_state, args=(context, neutron_router, p_con.Status.UPDATING)) self._dispatcher.dispatch_l3( d_context=embrane_ctx.DispatcherContext( p_con.Events.SET_NAT_RULE, neutron_router, context, state_change), args=(nat_info,)) return result def disassociate_floatingips(self, context, port_id, do_notify=True): try: fip_qry = context.session.query(l3_db.FloatingIP) floating_ip = fip_qry.filter_by(fixed_port_id=port_id).one() router_id = floating_ip["router_id"] except exc.NoResultFound: return router_ids = self._l3super.disassociate_floatingips( self, context, port_id, do_notify=do_notify) if router_id: neutron_router = self._get_router(context, router_id) fip_id = floating_ip["id"] state_change = operation.Operation( self._set_db_router_state, args=(context, neutron_router, p_con.Status.UPDATING)) self._dispatcher.dispatch_l3( d_context=embrane_ctx.DispatcherContext( p_con.Events.RESET_NAT_RULE, neutron_router, context, state_change), args=(fip_id,)) return router_ids