# Copyright 2014 Big Switch Networks, Inc. # All Rights Reserved. # # Copyright 2011 Nicira Networks, 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. # @author: Kevin Benton, kevin.benton@bigswitch.com import eventlet import sys import time from oslo.config import cfg from neutron.agent.linux import ovs_lib from neutron.agent.linux import utils from neutron.agent import rpc as agent_rpc from neutron.agent import securitygroups_rpc as sg_rpc from neutron.common import config from neutron.common import topics from neutron import context as q_context from neutron.extensions import securitygroup as ext_sg from neutron.openstack.common import excutils from neutron.openstack.common import log from neutron.openstack.common.rpc import dispatcher from neutron.plugins.bigswitch import config as pl_config LOG = log.getLogger(__name__) class IVSBridge(ovs_lib.OVSBridge): ''' This class does not provide parity with OVS using IVS. It's only the bare minimum necessary to use IVS with this agent. ''' def run_vsctl(self, args, check_error=False): full_args = ["ivs-ctl"] + args try: return utils.execute(full_args, root_helper=self.root_helper) except Exception as e: with excutils.save_and_reraise_exception() as ctxt: LOG.error(_("Unable to execute %(cmd)s. " "Exception: %(exception)s"), {'cmd': full_args, 'exception': e}) if not check_error: ctxt.reraise = False def get_vif_port_set(self): port_names = self.get_port_name_list() edge_ports = set(port_names) return edge_ports def get_vif_port_by_id(self, port_id): # IVS in nova uses hybrid method with last 14 chars of UUID name = 'qvo%s' % port_id[:14] if name in self.get_vif_port_set(): return name return False class PluginApi(agent_rpc.PluginApi, sg_rpc.SecurityGroupServerRpcApiMixin): pass class SecurityGroupAgent(sg_rpc.SecurityGroupAgentRpcMixin): def __init__(self, context, plugin_rpc, root_helper): self.context = context self.plugin_rpc = plugin_rpc self.root_helper = root_helper self.init_firewall() class RestProxyAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin): RPC_API_VERSION = '1.1' def __init__(self, integ_br, polling_interval, root_helper, vs='ovs'): super(RestProxyAgent, self).__init__() self.polling_interval = polling_interval self._setup_rpc() self.sg_agent = SecurityGroupAgent(self.context, self.plugin_rpc, root_helper) if vs == 'ivs': self.int_br = IVSBridge(integ_br, root_helper) else: self.int_br = ovs_lib.OVSBridge(integ_br, root_helper) def _setup_rpc(self): self.topic = topics.AGENT self.plugin_rpc = PluginApi(topics.PLUGIN) self.context = q_context.get_admin_context_without_session() self.dispatcher = dispatcher.RpcDispatcher([self]) consumers = [[topics.PORT, topics.UPDATE], [topics.SECURITY_GROUP, topics.UPDATE]] self.connection = agent_rpc.create_consumers(self.dispatcher, self.topic, consumers) def port_update(self, context, **kwargs): LOG.debug(_("Port update received")) port = kwargs.get('port') vif_port = self.int_br.get_vif_port_by_id(port['id']) if not vif_port: LOG.debug(_("Port %s is not present on this host."), port['id']) return LOG.debug(_("Port %s found. Refreshing firewall."), port['id']) if ext_sg.SECURITYGROUPS in port: self.sg_agent.refresh_firewall() def _update_ports(self, registered_ports): ports = self.int_br.get_vif_port_set() if ports == registered_ports: return added = ports - registered_ports removed = registered_ports - ports return {'current': ports, 'added': added, 'removed': removed} def _process_devices_filter(self, port_info): if 'added' in port_info: self.sg_agent.prepare_devices_filter(port_info['added']) if 'removed' in port_info: self.sg_agent.remove_devices_filter(port_info['removed']) def daemon_loop(self): ports = set() while True: start = time.time() try: port_info = self._update_ports(ports) if port_info: LOG.debug(_("Agent loop has new device")) self._process_devices_filter(port_info) ports = port_info['current'] except Exception: LOG.exception(_("Error in agent event loop")) elapsed = max(time.time() - start, 0) if (elapsed < self.polling_interval): time.sleep(self.polling_interval - elapsed) else: LOG.debug(_("Loop iteration exceeded interval " "(%(polling_interval)s vs. %(elapsed)s)!"), {'polling_interval': self.polling_interval, 'elapsed': elapsed}) def main(): eventlet.monkey_patch() cfg.CONF(project='neutron') config.setup_logging(cfg.CONF) pl_config.register_config() integ_br = cfg.CONF.RESTPROXYAGENT.integration_bridge polling_interval = cfg.CONF.RESTPROXYAGENT.polling_interval root_helper = cfg.CONF.AGENT.root_helper bsnagent = RestProxyAgent(integ_br, polling_interval, root_helper, cfg.CONF.RESTPROXYAGENT.virtual_switch_type) bsnagent.daemon_loop() sys.exit(0) if __name__ == "__main__": main()