Merge pull request #2 from changzhi1990/stethoclient
Implement get interface and move stethoclient position
This commit is contained in:
commit
8642df8fee
@ -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
|
12
setup.py
12
setup.py
@ -25,4 +25,14 @@ setup(name='stetho',
|
|||||||
license = "APL",
|
license = "APL",
|
||||||
keywords = ("stetho", "egg"),
|
keywords = ("stetho", "egg"),
|
||||||
platforms = "Independant",
|
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',
|
||||||
|
]
|
||||||
|
}
|
||||||
|
)
|
||||||
|
5
stetho/stethoclient/README.rst
Normal file
5
stetho/stethoclient/README.rst
Normal 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).
|
14
stetho/stethoclient/stethoclient/__init__.py
Normal file
14
stetho/stethoclient/stethoclient/__init__.py
Normal 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.
|
218
stetho/stethoclient/stethoclient/agent_api.py
Normal file
218
stetho/stethoclient/stethoclient/agent_api.py
Normal 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()
|
35
stetho/stethoclient/stethoclient/constants.py
Normal file
35
stetho/stethoclient/stethoclient/constants.py
Normal 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)
|
79
stetho/stethoclient/stethoclient/shell.py
Normal file
79
stetho/stethoclient/stethoclient/shell.py
Normal 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:]))
|
55
stetho/stethoclient/stethoclient/strutils.py
Normal file
55
stetho/stethoclient/stethoclient/strutils.py
Normal 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)
|
Loading…
x
Reference in New Issue
Block a user