Merge pull request #2 from changzhi1990/stethoclient

Implement get interface and move stethoclient position
This commit is contained in:
Damon.Wang 2015-12-30 17:47:35 +08:00
commit 8642df8fee
8 changed files with 429 additions and 1 deletions

View File

@ -0,0 +1,12 @@
[DEFAULT]
# Prefix of managed network. End of '.'
# Example: "10.0.4."
manage_network_prefix=127.0.0.
# Network nodes info. Just need sequence number.
# Example: 64, 65, 66
network_agents_info=64,65,66
# Compute nodes info. Just need sequence number.
# Example: 67, 68
compute_agents_info=67,68

View File

@ -25,4 +25,14 @@ setup(name='stetho',
license = "APL",
keywords = ("stetho", "egg"),
platforms = "Independant",
url = "https://www.ustack.com")
url = "https://www.ustack.com",
data_files=[
('/etc/stetho', ['etc/stetho.conf']),
],
entry_points={
'console_scripts': [
'stetho = stetho.stethoclient.shell:main',
'stetho-agent = stetho.agent.agent:main',
]
}
)

View File

@ -0,0 +1,5 @@
======================
Running Stethoclient
======================
This is a client library for stetho built on the Stetho API. It provides a Python API (the stethoclient module) and a command-line tool (stetho).

View File

@ -0,0 +1,14 @@
# Copyright 2015 UnitedStack, 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.

View File

@ -0,0 +1,218 @@
#!/usr/bin/python
# Copyright 2015 UnitedStack, 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 logging
import jsonrpclib
import sys
from cliff.command import Command
from cliff.lister import Lister
from json import JSONDecoder
from stetho.stethoclient.constants import AGENT_INFOS
LISTEN_PORT = 9698
def setup_server(agent):
log = logging.getLogger(__name__)
if agent in AGENT_INFOS:
log.debug('get agent:%s ip_address:%s' % (
agent, AGENT_INFOS[agent]))
else:
log.error('Agent %s not configured. Please check it.' % (
agent))
sys.exit()
log.debug('Begin create connection with http://%s:9698.' % (
agent))
server = jsonrpclib.Server('http://%s:%s' % (
AGENT_INFOS[agent], LISTEN_PORT))
log.debug('Create connection with %s success.' % (agent))
return server
class TearDownLink(Command):
"Delete a link"
log = logging.getLogger(__name__)
def get_parser(self, prog_name):
parser = super(TearDownLink, self).get_parser(prog_name)
parser.add_argument('agent', default='bad')
parser.add_argument('interface', default='.')
return parser
def take_action(self, parsed_args):
self.log.debug('Get parsed_args: %s' % parsed_args)
self.log.debug('Agent is %s' % parsed_args.agent)
self.log.debug('Interface is %s' % parsed_args.interface)
server = setup_server(parsed_args.agent)
try:
res = server.teardown_link(parsed_args.interface)
except Exception as e:
self.log.error('Error %s has occured.' % res)
class SetUpLink(Lister):
"Setup a link"
log = logging.getLogger(__name__)
def get_parser(self, prog_name):
parser = super(SetUpLink, self).get_parser(prog_name)
parser.add_argument('agent', default='bad')
parser.add_argument('interface', default='eth0')
parser.add_argument('cidr', default='.')
return parser
def take_action(self, parsed_args):
self.log.debug('Get parsed_args: %s' % parsed_args)
self.log.debug('Agent is %s' % parsed_args.agent)
self.log.debug('Interface is %s' % parsed_args.interface)
self.log.debug('Cidr is %s' % parsed_args.cidr)
server = setup_server(parsed_args.agent)
try:
# Setup Link
server.setup_link(parsed_args.interface,
parsed_args.cidr)
# Get Link info
res = server.get_interface(parsed_args.interface)
self.log.debug('Response is %s' % res)
if res['code'] == 0:
return (('Field', 'Value'),
((k, v) for k, v in res['data'].items()))
except Exception as e:
self.log.error('Agent %s return error: %s!' % parsed_args.agent, e)
sys.exit()
class GetInterface(Lister):
"A test function that show a message"
log = logging.getLogger(__name__)
def get_parser(self, prog_name):
parser = super(GetInterface, self).get_parser(prog_name)
parser.add_argument('agent', default='bad')
parser.add_argument('interface', default='eth0')
return parser
def take_action(self, parsed_args):
self.log.debug('Get parsed_args: %s' % parsed_args)
self.log.debug('Agent is %s' % parsed_args.agent)
self.log.debug('Interface is %s' % parsed_args.interface)
server = setup_server(parsed_args.agent)
try:
res = server.get_interface(parsed_args.interface)
self.log.debug('Response is %s' % res)
if res['code'] == 0:
return (('Field', 'Value'),
((k, v) for k, v in res['data'].items()))
except:
self.log.error('Agent %s return error!' % parsed_args.agent)
sys.exit()
class AddVlanToInterface(Lister):
"Setup a link"
log = logging.getLogger(__name__)
def get_parser(self, prog_name):
parser = super(AddVlanToInterface, self).get_parser(prog_name)
parser.add_argument('agent', default='bad')
parser.add_argument('interface', default='eth0')
parser.add_argument('vlan_id', default='1124')
return parser
def take_action(self, parsed_args):
self.log.debug('Get parsed_args: %s' % parsed_args)
self.log.debug('Agent is %s' % parsed_args.agent)
self.log.debug('Interface is %s' % parsed_args.interface)
self.log.debug('Vlan_id is %s' % parsed_args.vlan_id)
server = setup_server(parsed_args.agent)
try:
# Setup Link
server.add_vlan_to_interface(parsed_args.interface,
parsed_args.vlan_id)
# Get Link info
new_interface = parsed_args.interface + '.' + parsed_args.vlan_id
res = server.get_interface(new_interface)
self.log.debug('Response is %s' % res)
if res['code'] == 0:
return (('Field', 'Value'),
((k, v) for k, v in res['data'].items()))
except Exception as e:
self.log.error('Agent %s return error: %s!' % parsed_args.agent, e)
sys.exit()
class AgentPing(Lister):
"Ping a destination from one agent"
log = logging.getLogger(__name__)
def get_parser(self, prog_name):
parser = super(AgentPing, self).get_parser(prog_name)
parser.add_argument('agent', default='bad')
parser.add_argument('destination', default='1.2.4.8')
parser.add_argument('--count', nargs='?', default='2')
parser.add_argument('--timeout', nargs='?', default='2')
parser.add_argument('--interface', nargs='?', default='eth0')
return parser
def take_action(self, parsed_args):
self.log.debug('Get parsed_args: %s' % parsed_args)
server = setup_server(parsed_args.agent)
try:
dest = parsed_args.destination.split(',')
res = server.ping(ips=dest,
count=parsed_args.count,
timeout=parsed_args.timeout,
interface=parsed_args.interface)
self.log.debug('Response is %s' % res)
if res['code'] == 0:
return (('Destination', 'Packet Loss (%)'),
((k, v) for k, v in res['data'].items()))
except Exception as e:
self.log.error('Agent %s return error: %s!' % parsed_args.agent, e)
sys.exit()
class CheckPortsOnBr(Lister):
"Check a port if exists on a ovs bridge"
log = logging.getLogger(__name__)
def get_parser(self, prog_name):
parser = super(CheckPortsOnBr, self).get_parser(prog_name)
parser.add_argument('agent', default='bad')
parser.add_argument('bridge', default='br-int')
parser.add_argument('port', default='br-int')
return parser
def take_action(self, parsed_args):
self.log.debug('Get parsed_args: %s' % parsed_args)
server = setup_server(parsed_args.agent)
try:
res = server.check_ports_on_br(parsed_args.bridge,
parsed_args.port)
self.log.debug('Response is %s' % res)
if res['code'] == 0:
return (('Port', 'Exists'),
((k, v) for k, v in res['data'].items()))
except Exception as e:
self.log.error('Agent %s return error: %s!' % parsed_args.agent, e)
sys.exit()

View File

@ -0,0 +1,35 @@
# Copyright 2015 UnitedStack, 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 oslo_config import cfg
OPTS = [
cfg.ListOpt('network_agents_info', default=[],
help="Mappings of network agents and stetho listened IP."),
cfg.ListOpt('compute_agents_info', default=[],
help="Mappings of compute agents and stetho listened IP."),
cfg.StrOpt('managed_network_prefix', default='127.0.0.',
help="Managed network prefix."),
]
cfg.CONF.register_opts(OPTS)
cfg.CONF([], project='stetho',
default_config_files=['/etc/stetho/stetho.conf'])
AGENT_INFOS = {}
all_agents = cfg.CONF.network_agents_info + cfg.CONF.compute_agents_info
for agent in all_agents:
item = {'agent-'+agent: cfg.CONF.managed_network_prefix+agent}
AGENT_INFOS.update(item)

View File

@ -0,0 +1,79 @@
# Copyright 2015 UnitedStack, 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.
"""
Command-line interface to Stetho APIs
"""
import sys
import logging
from cliff import app
from cliff import commandmanager
from stetho.stethoclient import agent_api
from stetho.stethoclient import strutils
VERSION = '0.1'
STETHO_API_VERSION = '0.1'
COMMAND_V1 = {
'setup-link': agent_api.SetUpLink,
'teardown-link': agent_api.TearDownLink,
'add-vlan-to-interface': agent_api.AddVlanToInterface,
'ping': agent_api.AgentPing,
'check-ports-on-br': agent_api.CheckPortsOnBr,
'get-interface': agent_api.GetInterface,
}
COMMANDS = {'0.1': COMMAND_V1}
class StethoShell(app.App):
def __init__(self, apiversion):
super(StethoShell, self).__init__(
description=__doc__.strip(),
version=VERSION,
command_manager=commandmanager.CommandManager('stetho.cli'),
)
self.commands = COMMANDS
for k, v in self.commands[apiversion].items():
self.command_manager.add_command(k, v)
def initialize_app(self, argv):
self.LOG.debug('initialize_app')
def prepare_to_run_command(self, cmd):
self.LOG.debug('prepare_to_run_command %s', cmd.__class__.__name__)
def clean_up(self, cmd, result, err):
self.LOG.debug('clean_up %s', cmd.__class__.__name__)
if err:
self.LOG.debug('got an error: %s', err)
def main(argv=sys.argv[1:]):
try:
return StethoShell(STETHO_API_VERSION).run(
list(map(strutils.safe_decode, argv)))
except KeyboardInterrupt:
print "... terminating neutron client"
return 1
except Exception as e:
print(e)
return 1
if __name__ == "__main__":
sys.exit(main(sys.argv[1:]))

View File

@ -0,0 +1,55 @@
# Copyright 2015 UnitedStack, 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 six
import sys
def safe_decode(text, incoming=None, errors='strict'):
"""Decodes incoming str using `incoming` if they're not already unicode.
:param incoming: Text's current encoding
:param errors: Errors handling policy. See here for valid
values http://docs.python.org/2/library/codecs.html
:returns: text or a unicode `incoming` encoded
representation of it.
:raises TypeError: If text is not an isntance of str
"""
if not isinstance(text, six.string_types):
raise TypeError("%s can't be decoded" % type(text))
if isinstance(text, six.text_type):
return text
if not incoming:
incoming = (sys.stdin.encoding or
sys.getdefaultencoding())
try:
return text.decode(incoming, errors)
except UnicodeDecodeError:
# Note(flaper87) If we get here, it means that
# sys.stdin.encoding / sys.getdefaultencoding
# didn't return a suitable encoding to decode
# text. This happens mostly when global LANG
# var is not set correctly and there's no
# default encoding. In this case, most likely
# python will use ASCII or ANSI encoders as
# default encodings but they won't be capable
# of decoding non-ASCII characters.
#
# Also, UTF-8 is being used since it's an ASCII
# extension.
return text.decode('utf-8', errors)