Huang Rui cf77385929 The init commmit of stackforge/zvm-driver
Including all python modules for nova-zvm-virt-driver and
neutron-zvm-plugin.

Change-Id: I72dd9f64fc412cbf10f5e7ab6e4ac465a977e849
2014-11-04 17:03:02 +08:00

351 lines
14 KiB
Python
Executable File

# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2014 IBM Corp.
#
# 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.
import eventlet
import sys
import time
import zvm_network
from oslo.config import cfg
from neutron import context
from neutron.agent import rpc as agent_rpc
from neutron.common import config as common_config
from neutron.common import constants as q_const
from neutron.common import rpc as n_rpc
from neutron.common import topics
from neutron.plugins.common import constants as p_const
from neutron.openstack.common import log as logging
from neutron.openstack.common import loopingcall
from neutron.openstack.common.gettextutils import _
from neutron.plugins.zvm.common import exception
from neutron.plugins.zvm.common import utils
LOG = logging.getLogger(__name__)
def restart_wrapper(func):
def wrapper(*args, **kw):
gen = func(*args, **kw)
gen.next()
return gen
return wrapper
class zvmNeutronAgent(n_rpc.RpcCallback):
RPC_API_VERSION = '1.1'
def __init__(self):
super(zvmNeutronAgent, self).__init__()
self._utils = utils.zvmUtils()
self._polling_interval = cfg.CONF.AGENT.polling_interval
self._zhcp_node = cfg.CONF.AGENT.xcat_zhcp_nodename
self._host = cfg.CONF.AGENT.zvm_host
zvm_net = zvm_network.zvmNetwork()
self.agent_state = {
'binary': 'neutron-zvm-agent',
'host': self._host,
'topic': q_const.L2_AGENT_TOPIC,
'configurations': {'vswitch_mappings': zvm_net.get_network_maps()},
'agent_type': q_const.AGENT_TYPE_ZVM,
'start_flag': True}
self._setup_server_rpc()
self._zhcp_userid = self._utils.get_zhcp_userid(self._zhcp_node)
self._restart_handler = self._handle_restart()
def _setup_server_rpc(self):
self.agent_id = 'zvm_agent_%s' % self._zhcp_node
self.topic = topics.AGENT
self.plugin_rpc = agent_rpc.PluginApi(topics.PLUGIN)
self.state_rpc = agent_rpc.PluginReportStateAPI(topics.PLUGIN)
self.context = context.get_admin_context_without_session()
self.endpoints = [self]
consumers = [[topics.PORT, topics.UPDATE],
[topics.NETWORK, topics.DELETE]]
self.connection = agent_rpc.create_consumers(self.endpoints,
self.topic,
consumers)
report_interval = cfg.CONF.AGENT.report_interval
if report_interval:
heartbeat = loopingcall.FixedIntervalLoopingCall(
self._report_state)
heartbeat.start(interval=report_interval)
def _report_state(self):
try:
self.state_rpc.report_state(self.context, self.agent_state)
self.agent_state.pop('start_flag', None)
except Exception:
LOG.exception(_("Failed reporting state!"))
def network_delete(self, context, network_id=None):
LOG.debug(_("Network delete received. UUID: %s"), network_id)
def port_update(self, context, **kwargs):
port = kwargs.get('port')
LOG.debug(_("Port update received. UUID: %s"), port)
if not port['id'] in self._port_map.keys():
# update a port which is not coupled to any NIC, nothing
# to do for a user based vswitch
return
vswitch = self._port_map[port['id']]['vswitch']
userid = self._port_map[port['id']]['userid']
if port['admin_state_up']:
self._utils.couple_nic_to_vswitch(vswitch, port['id'],
self._zhcp_node, userid)
self.plugin_rpc.update_device_up(self.context, port['id'],
self.agent_id)
else:
self._utils.uncouple_nic_from_vswitch(vswitch, port['id'],
self._zhcp_node, userid)
self.plugin_rpc.update_device_down(self.context, port['id'],
self.agent_id)
self._utils.put_user_direct_online(self._zhcp_node,
self._zhcp_userid)
def port_bound(self, port_id, net_uuid,
network_type, physical_network, segmentation_id, userid):
LOG.debug(_("Binding port %s"), port_id)
self._utils.grant_user(self._zhcp_node, physical_network, userid)
vdev = self._utils.couple_nic_to_vswitch(physical_network, port_id,
self._zhcp_node, userid)
self._utils.put_user_direct_online(self._zhcp_node,
self._zhcp_userid)
if network_type == p_const.TYPE_VLAN:
LOG.info(_('Binding VLAN, VLAN ID: %s'), segmentation_id)
self._utils.set_vswitch_port_vlan_id(segmentation_id, port_id,
vdev, self._zhcp_node,
physical_network)
else:
LOG.info(_('Bind %s mode done'), network_type)
def port_unbound(self, port_id):
LOG.debug(_("Unbinding port %s"), port_id)
# uncouple is not necessary, because revoke user will uncouple it
# automatically.
self._utils.revoke_user(self._zhcp_node,
self._port_map[port_id]['vswitch'],
self._port_map[port_id]['userid'])
def _update_ports(self, registered_ports):
ports_info = self._utils.get_nic_ids()
ports = set()
for p in ports_info:
target_host = p.split(',')[5].strip('"')
new_port_id = p.split(',')[2].strip('"')
if target_host == self._zhcp_node:
ports.add(new_port_id)
if ports == registered_ports:
return
added = ports - registered_ports
removed = registered_ports - ports
return {'current': ports, 'added': added, 'removed': removed}
def _treat_vif_port(self, port_id, network_id, network_type,
physical_network, segmentation_id,
admin_state_up):
node = self._utils.get_node_from_port(port_id)
userid = self._utils.get_userid_from_node(node)
LOG.info(_("Update port for node:%s") % node)
if admin_state_up:
self.port_bound(port_id, network_id, network_type,
physical_network, segmentation_id,
userid)
else:
self._utils.grant_user(self._zhcp_node, physical_network, userid)
return (node, userid)
def _treat_devices_added(self, devices):
for device in devices:
LOG.info(_("Adding port %s") % device)
try:
details = self.plugin_rpc.get_device_details(self.context,
device,
self.agent_id)
except Exception:
LOG.debug(_("Unable to get port details for %s:"), device)
continue
try:
if 'port_id' in details:
LOG.info(_("Port %(device)s updated."
" Details: %(details)s"),
{'device': device, 'details': details})
(node, userid) = self._treat_vif_port(
details['port_id'],
details['network_id'],
details['network_type'],
details['physical_network'],
details['segmentation_id'],
details['admin_state_up'])
# add device done, keep port map info
self._port_map[device] = {}
self._port_map[device]['userid'] = userid
self._port_map[device]['nodename'] = node
self._port_map[device]['vswitch'] = details[
'physical_network']
self._port_map[device]['vlan_id'] = details[
'segmentation_id']
# no rollback if this fails
self._utils.update_xcat_switch(details['port_id'],
details['physical_network'],
details['segmentation_id'])
if details.get('admin_state_up'):
LOG.debug(_("Setting status for %s to UP"), device)
self.plugin_rpc.update_device_up(
self.context, device, self.agent_id, cfg.CONF.host)
else:
LOG.debug(_("Setting status for %s to DOWN"), device)
self.plugin_rpc.update_device_down(
self.context, device, self.agent_id, cfg.CONF.host)
else:
LOG.debug(_("Device %s not defined on Neutron server"),
device)
continue
except Exception as e:
LOG.exception(_("Can not add device %(device)s: %(msg)s"),
{'device': device, 'msg': e})
continue
def _treat_devices_removed(self, devices):
for device in devices:
LOG.info(_("Removing port %s"), device)
try:
if not device in self._port_map:
LOG.warn(_("Can't find port %s in zvm agent"), device)
continue
self.port_unbound(device)
self.plugin_rpc.update_device_down(self.context,
device,
self.agent_id)
del self._port_map[device]
except Exception as e:
LOG.exception(_("Removing port failed %(device)s: %(msg)s"),
{'device': device, 'msg': e})
continue
def _process_network_ports(self, port_info):
if len(port_info['added']):
self._treat_devices_added(port_info['added'])
if len(port_info['removed']):
self._treat_devices_removed(port_info['removed'])
def xcatdb_daemon_loop(self):
ports = set()
# Get all exsited ports as configured
all_ports_info = self._update_ports(ports)
if all_ports_info is not None:
ports = all_ports_info['current']
connect = True
while True:
try:
start_time = time.time()
port_info = self._update_ports(ports)
# if no exception is raised in _update_ports,
# then the connection has recovered
if connect is False:
self._restart_handler.send(None)
connect = True
if port_info:
LOG.debug(_("Devices change!"))
self._process_network_ports(port_info)
ports = port_info['current']
except exception.zVMxCatRequestFailed as e:
LOG.error(_("Lost connection to xCAT. %s"), e)
connect = False
except Exception as e:
LOG.exception(_("error in xCAT DB query loop: %s"), e)
# sleep till end of polling interval
elapsed = (time.time() - start_time)
if (elapsed < self._polling_interval):
sleep_time = self._polling_interval - elapsed
LOG.debug(_("Sleep %s"), sleep_time)
time.sleep(sleep_time)
else:
LOG.debug(_("Looping iteration exceeded interval"))
def _init_xcat_mgt(self):
'''xCAT Management Node(MN) use the first flat network to manage all
the instances. So a flat network is required.
To talk to xCAT MN, xCAT MN requires every instance has a NIC which is
in the same subnet as xCAT. The xCAT MN's IP address is xcat_mgt_ip,
mask is xcat_mgt_mask in the config file,
by default neutron_zvm_plugin.ini.
'''
if not len(cfg.CONF.ml2_type_flat.flat_networks):
raise exception.zvmException(
msg=_('Can not find xCAT management network,'
'a flat network is required by xCAT.'))
self._utils.create_xcat_mgt_network(self._zhcp_node,
cfg.CONF.AGENT.xcat_mgt_ip,
cfg.CONF.AGENT.xcat_mgt_mask,
cfg.CONF.ml2_type_flat.flat_networks[0])
@restart_wrapper
def _handle_restart(self):
xcat_uptime, zvm_uptime = (None, None)
while True:
LOG.info(_("Try to reinitialize network ... "))
try:
tmp_new_time = self._utils.query_xcat_uptime(self._zhcp_node)
if xcat_uptime != tmp_new_time:
self._init_xcat_mgt()
xcat_uptime = tmp_new_time
tmp_new_time = self._utils.query_zvm_uptime(self._zhcp_node)
if zvm_uptime != tmp_new_time:
self._port_map = self._utils.re_grant_user(self._zhcp_node)
zvm_uptime = tmp_new_time
yield
except Exception:
LOG.error(_("Failed to handle restart,"
"try again in 5 seconds"))
time.sleep(5)
def main():
eventlet.monkey_patch()
cfg.CONF(project='neutron')
common_config.init(sys.argv[1:])
common_config.setup_logging()
agent = zvmNeutronAgent()
# Start to query xCAT DB
agent.xcatdb_daemon_loop()
LOG.info(_("z/VM agent initialized, now running... "))
sys.exit(0)