Merge pull request #153 from kilogram/diagnostics

Diagnostics Extensions
This commit is contained in:
Amir Sadoughi
2013-08-07 12:31:02 -07:00
7 changed files with 230 additions and 1 deletions

View File

@@ -0,0 +1,62 @@
# Copyright (c) 2013 OpenStack Foundation
#
# 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 functools
from neutron.api import extensions
from neutron.common import exceptions
from neutron import manager
from neutron.openstack.common import log as logging
LOG = logging.getLogger("quark.diagnostics")
class Diagnostician(object):
def __init__(self, plugin):
self.plugin = plugin
def diag_not_implemented(self, res, id, input):
LOG.warning("Diagnostics not implemented on resource %ss." % res)
raise exceptions.ServiceUnavailable()
def diagnose(self, res, input, req, id):
LOG.debug("Requested diagnostics fields %s on resource %s with id %s"
% (input['diag'], res, id))
return getattr(
self.plugin, 'diagnose_%s' % res.replace('-', '_'),
functools.partial(self.diag_not_implemented, res))(
req.context, id, input['diag'])
class Diagnostics(extensions.ExtensionDescriptor):
def get_name(self):
return "Diagnostics"
def get_alias(self):
return "diagnostics"
def get_description(self):
return "Diagnostics extension"
def get_namespace(self):
return "None"
def get_updated(self):
return "never"
def get_actions(self):
diagnose = Diagnostician(manager.NeutronManager.get_plugin()).diagnose
resources = ['port', 'subnet', 'network']
return (extensions.ActionExtension('%ss' % res, 'diag',
functools.partial(diagnose, res)) for res in resources)

View File

@@ -37,6 +37,10 @@ class BaseDriver(object):
def delete_network(self, context, network_id):
LOG.info("delete_network %s" % network_id)
def diag_network(self, context, network_id, **kwargs):
LOG.info("diag_network %s" % network_id)
return {}
def create_port(self, context, network_id, port_id, **kwargs):
LOG.info("create_port %s %s %s" % (context.tenant_id, network_id,
port_id))
@@ -49,6 +53,10 @@ class BaseDriver(object):
def delete_port(self, context, port_id, **kwargs):
LOG.info("delete_port %s %s" % (context.tenant_id, port_id))
def diag_port(self, context, network_id, **kwargs):
LOG.info("diag_port %s" % network_id)
return {}
def create_security_group(self, context, group_name, **group):
LOG.info("Creating security profile %s for tenant %s" %
(group_name, context.tenant_id))

View File

@@ -60,6 +60,14 @@ physical_net_type_map = {
CONF.register_opts(nvp_opts, "NVP")
def _tag_roll(tags):
return [{'scope': k, 'tag': v} for k, v in tags]
def _tag_unroll(tags):
return dict((t['scope'], t['tag']) for t in tags)
class NVPDriver(base.BaseDriver):
def __init__(self):
self.nvp_connections = []
@@ -117,6 +125,31 @@ class NVPDriver(base.BaseDriver):
LOG.debug("Deleting lswitch %s" % switch["uuid"])
connection.lswitch(switch["uuid"]).delete()
def _collect_lswitch_info(self, lswitch, get_status):
info = {
'port_isolation_enabled': lswitch['port_isolation_enabled'],
'display_name': lswitch['display_name'],
'uuid': lswitch['uuid'],
'transport_zones': lswitch['transport_zones'],
}
info.update(_tag_unroll(lswitch['tags']))
if get_status:
status = lswitch.pop('_relations')['LogicalSwitchStatus']
info.update({
'lport_stats': {
'fabric_up': status['lport_fabric_up_count'],
'admin_up': status['lport_admin_up_count'],
'link_up': status['lport_link_up_count'],
'count': status['lport_count'],
}, 'fabric_status': status['fabric_status'],
})
return info
def diag_network(self, context, network_id, get_status):
switches = self._lswitch_status_query(context, network_id)['results']
return {'logical_switches': [self._collect_lswitch_info(s, get_status)
for s in switches]}
def create_port(self, context, network_id, port_id,
status=True, security_groups=[], allowed_pairs=[]):
tenant_id = context.tenant_id
@@ -159,6 +192,67 @@ class NVPDriver(base.BaseDriver):
LOG.debug("Deleting port %s from lswitch %s" % (port_id, lswitch_uuid))
connection.lswitch_port(lswitch_uuid, port_id).delete()
def _collect_lport_info(self, lport, get_status):
info = {
'mirror_targets': lport['mirror_targets'],
'display_name': lport['display_name'],
'portno': lport['portno'],
'allowed_address_pairs': lport['allowed_address_pairs'],
'nvp_security_groups': lport['security_profiles'],
'uuid': lport['uuid'],
'admin_status_enabled': lport['admin_status_enabled'],
'queue_uuid': lport['queue_uuid'],
}
if get_status:
stats = lport['statistics']
status = lport['status']
lswitch = {
'uuid': status['lswitch']['uuid'],
'display_name': status['lswitch']['display_name'],
}
lswitch.update(_tag_unroll(status['lswitch']['tags']))
info.update({
'statistics': {
'recieved': {
'packets': stats['rx_packets'],
'bytes': stats['rx_bytes'],
'errors': stats['rx_errors']
},
'transmitted': {
'packets': stats['tx_packets'],
'bytes': stats['tx_bytes'],
'errors': stats['tx_errors']
},
},
'status': {
'link_status_up': status['link_status_up'],
'admin_status_up': status['admin_status_up'],
'fabric_status_up': status['fabric_status_up'],
},
'lswitch': lswitch,
})
info.update(_tag_unroll(lport['tags']))
return info
def diag_port(self, context, port_id, get_status=False):
connection = self.get_connection()
lswitch_uuid = self._lswitch_from_port(context, port_id)
lswitch_port = connection.lswitch_port(lswitch_uuid, port_id)
query = lswitch_port.query()
query.relations("LogicalPortAttachment")
results = query.results()
if results['result_count'] == 0:
return {'lport': "Logical port not found."}
config = results['results'][0]
relations = config.pop('_relations')
config['attachment'] = relations['LogicalPortAttachment']['type']
if get_status:
config['status'] = lswitch_port.status()
config['statistics'] = lswitch_port.statistics()
return {'lport': self._collect_lport_info(config, get_status)}
def _get_network_details(self, context, network_id, switches):
name, phys_net, phys_type, segment_id = None, None, None, None
for res in switches["results"]:

View File

@@ -75,7 +75,7 @@ class Plugin(neutron_plugin_base_v2.NeutronPluginBaseV2,
sg_ext.SecurityGroupPluginBase):
supported_extension_aliases = ["mac_address_ranges", "routes",
"ip_addresses", "ports_quark",
"security-group",
"security-group", "diagnostics",
"subnets_quark", "provider",
"ip_policies", "quotas"]
@@ -188,6 +188,9 @@ class Plugin(neutron_plugin_base_v2.NeutronPluginBaseV2,
def disassociate_port(self, context, id, ip_address_id):
return ports.disassociate_port(context, id, ip_address_id)
def diagnose_port(self, context, id, fields):
return ports.diagnose_port(context, id, fields)
def get_route(self, context, id):
return routes.get_route(context, id)
@@ -218,6 +221,9 @@ class Plugin(neutron_plugin_base_v2.NeutronPluginBaseV2,
def delete_subnet(self, context, id):
return subnets.delete_subnet(context, id)
def diagnose_subnet(self, context, id, fields):
return subnets.diagnose_subnet(context, id, fields)
def create_network(self, context, network):
return networks.create_network(context, network)
@@ -235,3 +241,6 @@ class Plugin(neutron_plugin_base_v2.NeutronPluginBaseV2,
def delete_network(self, context, id):
return networks.delete_network(context, id)
def diagnose_network(self, context, id, fields):
return networks.diagnose_network(context, id, fields)

View File

@@ -24,6 +24,7 @@ from oslo.config import cfg
from quark.db import api as db_api
from quark import network_strategy
from quark.plugin_modules import ports
from quark.plugin_modules import security_groups
from quark.plugin_modules import subnets
from quark import plugin_views as v
@@ -202,3 +203,31 @@ def delete_network(context, id):
for subnet in net["subnets"]:
subnets._delete_subnet(context, subnet)
db_api.network_delete(context, net)
def _diag_network(context, network, fields):
if not network:
return False
net = v._make_network_dict(network)
net['ports'] = [p.get('id') for p in network.get('ports', [])]
if 'subnets' in fields:
net['subnets'] = [subnets.diagnose_subnet(context, s, fields)
for s in network.get('subnets', [])]
if 'ports' in fields:
net['ports'] = [ports.diagnose_port(context, s, fields)
for s in net['ports']]
if 'config' in fields or 'status' in fields:
net.update(net_driver.diag_network(
context, net['id'], get_status='status' in fields))
return net
def diagnose_network(context, id, fields):
if id == "*":
return {'networks': [_diag_network(context, net, fields) for
net in db_api.network_find(context).all()]}
db_net = db_api.network_find(context, id=id, scope=db_api.ONE)
if not db_net:
raise exceptions.NetworkNotFound(net_id=id)
net = _diag_network(context, db_net, fields)
return {'networks': net}

View File

@@ -322,3 +322,24 @@ def disassociate_port(context, id, ip_address_id):
if len(the_address["ports"]) == 0:
the_address["deallocated"] = 1
return v._make_port_dict(port)
def _diag_port(context, port, fields):
if not port:
return False
p = v._make_port_dict(port)
if 'config' in fields:
p.update(net_driver.diag_port(
context, port["backend_key"], get_status='status' in fields))
return p
def diagnose_port(context, id, fields):
if id == "*":
return {'ports': [_diag_port(context, port, fields) for
port in db_api.port_find(context).all()]}
db_port = db_api.port_find(context, id=id, scope=db_api.ONE)
if not db_port:
raise exceptions.PortNotFound(port_id=id, net_id='')
port = _diag_port(context, db_port, fields)
return {'ports': port}

View File

@@ -277,3 +277,9 @@ def delete_subnet(context, id):
if not subnet:
raise exceptions.SubnetNotFound(subnet_id=id)
_delete_subnet(context, subnet)
def diagnose_subnet(context, id, fields):
if id == "*":
return {'subnets': get_subnets(context, filters={})}
return {'subnets': get_subnet(context, id)}