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",
|
||||
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',
|
||||
]
|
||||
}
|
||||
)
|
||||
|
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