5d02de7d63
When probe is created it needs to have correct bindings otherwise port is marked as dead. Change-Id: I64441fbe802aab068c129c9647c7144fcd4c50a1 Partial-Bug: #1262785
203 lines
7.8 KiB
Python
203 lines
7.8 KiB
Python
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
|
|
|
# Copyright 2012, Nachi Ueno, NTT MCL, 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.
|
|
|
|
import shlex
|
|
import socket
|
|
|
|
import netaddr
|
|
from oslo.config import cfg
|
|
|
|
from neutron.agent.common import config
|
|
from neutron.agent.linux.dhcp import DictModel
|
|
from neutron.agent.linux import ip_lib
|
|
from neutron.agent.linux import utils
|
|
from neutron.openstack.common import log as logging
|
|
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
DEVICE_OWNER_NETWORK_PROBE = 'network:probe'
|
|
|
|
DEVICE_OWNER_COMPUTE_PROBE = 'compute:probe'
|
|
|
|
|
|
class NeutronDebugAgent():
|
|
|
|
OPTS = [
|
|
# Needed for drivers
|
|
cfg.BoolOpt('use_namespaces', default=True,
|
|
help=_("Use Linux network namespaces")),
|
|
cfg.StrOpt('interface_driver',
|
|
help=_("The driver used to manage the virtual "
|
|
"interface.")),
|
|
cfg.StrOpt('external_network_bridge', default='br-ex',
|
|
help=_("Name of bridge used for external network "
|
|
"traffic.")),
|
|
]
|
|
|
|
def __init__(self, conf, client, driver):
|
|
self.conf = conf
|
|
self.root_helper = config.get_root_helper(conf)
|
|
self.client = client
|
|
self.driver = driver
|
|
|
|
def _get_namespace(self, port):
|
|
return "qprobe-%s" % port.id
|
|
|
|
def create_probe(self, network_id, device_owner='network'):
|
|
network = self._get_network(network_id)
|
|
bridge = None
|
|
if network.external:
|
|
bridge = self.conf.external_network_bridge
|
|
|
|
port = self._create_port(network, device_owner)
|
|
interface_name = self.driver.get_device_name(port)
|
|
namespace = None
|
|
if self.conf.use_namespaces:
|
|
namespace = self._get_namespace(port)
|
|
|
|
if ip_lib.device_exists(interface_name, self.root_helper, namespace):
|
|
LOG.debug(_('Reusing existing device: %s.'), interface_name)
|
|
else:
|
|
self.driver.plug(network.id,
|
|
port.id,
|
|
interface_name,
|
|
port.mac_address,
|
|
bridge=bridge,
|
|
namespace=namespace)
|
|
ip_cidrs = []
|
|
for fixed_ip in port.fixed_ips:
|
|
subnet = fixed_ip.subnet
|
|
net = netaddr.IPNetwork(subnet.cidr)
|
|
ip_cidr = '%s/%s' % (fixed_ip.ip_address, net.prefixlen)
|
|
ip_cidrs.append(ip_cidr)
|
|
self.driver.init_l3(interface_name, ip_cidrs, namespace=namespace)
|
|
return port
|
|
|
|
def _get_subnet(self, subnet_id):
|
|
subnet_dict = self.client.show_subnet(subnet_id)['subnet']
|
|
return DictModel(subnet_dict)
|
|
|
|
def _get_network(self, network_id):
|
|
network_dict = self.client.show_network(network_id)['network']
|
|
network = DictModel(network_dict)
|
|
network.external = network_dict.get('router:external')
|
|
obj_subnet = [self._get_subnet(s_id) for s_id in network.subnets]
|
|
network.subnets = obj_subnet
|
|
return network
|
|
|
|
def clear_probe(self):
|
|
ports = self.client.list_ports(
|
|
device_id=socket.gethostname(),
|
|
device_owner=[DEVICE_OWNER_NETWORK_PROBE,
|
|
DEVICE_OWNER_COMPUTE_PROBE])
|
|
info = ports['ports']
|
|
for port in info:
|
|
self.delete_probe(port['id'])
|
|
|
|
def delete_probe(self, port_id):
|
|
port = DictModel(self.client.show_port(port_id)['port'])
|
|
network = self._get_network(port.network_id)
|
|
bridge = None
|
|
if network.external:
|
|
bridge = self.conf.external_network_bridge
|
|
ip = ip_lib.IPWrapper(self.root_helper)
|
|
namespace = self._get_namespace(port)
|
|
if self.conf.use_namespaces and ip.netns.exists(namespace):
|
|
self.driver.unplug(self.driver.get_device_name(port),
|
|
bridge=bridge,
|
|
namespace=namespace)
|
|
try:
|
|
ip.netns.delete(namespace)
|
|
except Exception:
|
|
LOG.warn(_('Failed to delete namespace %s'), namespace)
|
|
else:
|
|
self.driver.unplug(self.driver.get_device_name(port),
|
|
bridge=bridge)
|
|
self.client.delete_port(port.id)
|
|
|
|
def list_probes(self):
|
|
ports = self.client.list_ports(
|
|
device_owner=[DEVICE_OWNER_NETWORK_PROBE,
|
|
DEVICE_OWNER_COMPUTE_PROBE])
|
|
info = ports['ports']
|
|
for port in info:
|
|
port['device_name'] = self.driver.get_device_name(DictModel(port))
|
|
return info
|
|
|
|
def exec_command(self, port_id, command=None):
|
|
port = DictModel(self.client.show_port(port_id)['port'])
|
|
ip = ip_lib.IPWrapper(self.root_helper)
|
|
namespace = self._get_namespace(port)
|
|
if self.conf.use_namespaces:
|
|
if not command:
|
|
return "sudo ip netns exec %s" % self._get_namespace(port)
|
|
namespace = ip.ensure_namespace(namespace)
|
|
return namespace.netns.execute(shlex.split(command))
|
|
else:
|
|
return utils.execute(shlex.split(command))
|
|
|
|
def ensure_probe(self, network_id):
|
|
ports = self.client.list_ports(network_id=network_id,
|
|
device_id=socket.gethostname(),
|
|
device_owner=DEVICE_OWNER_NETWORK_PROBE)
|
|
info = ports.get('ports', [])
|
|
if info:
|
|
return DictModel(info[0])
|
|
else:
|
|
return self.create_probe(network_id)
|
|
|
|
def ping_all(self, network_id=None, timeout=1):
|
|
if network_id:
|
|
ports = self.client.list_ports(network_id=network_id)['ports']
|
|
else:
|
|
ports = self.client.list_ports()['ports']
|
|
result = ""
|
|
for port in ports:
|
|
probe = self.ensure_probe(port['network_id'])
|
|
if port['device_owner'] == DEVICE_OWNER_NETWORK_PROBE:
|
|
continue
|
|
for fixed_ip in port['fixed_ips']:
|
|
address = fixed_ip['ip_address']
|
|
subnet = self._get_subnet(fixed_ip['subnet_id'])
|
|
if subnet.ip_version == 4:
|
|
ping_command = 'ping'
|
|
else:
|
|
ping_command = 'ping6'
|
|
result += self.exec_command(probe.id,
|
|
'%s -c 1 -w %s %s' % (ping_command,
|
|
timeout,
|
|
address))
|
|
return result
|
|
|
|
def _create_port(self, network, device_owner):
|
|
host = self.conf.host
|
|
body = {'port': {'admin_state_up': True,
|
|
'network_id': network.id,
|
|
'device_id': '%s' % socket.gethostname(),
|
|
'device_owner': '%s:probe' % device_owner,
|
|
'tenant_id': network.tenant_id,
|
|
'binding:host_id': host,
|
|
'fixed_ips': [dict(subnet_id=s.id)
|
|
for s in network.subnets]}}
|
|
port_dict = self.client.create_port(body)['port']
|
|
port = DictModel(port_dict)
|
|
port.network = network
|
|
for fixed_ip in port.fixed_ips:
|
|
fixed_ip.subnet = self._get_subnet(fixed_ip.subnet_id)
|
|
return port
|