Remove CLI parts from os-ken

It has been agreed on one of the Neutron team meetings [1] that
we should remove those parts of os-ken which provides CLI scripts
and make it as small as possible to just fit Neutron actual needs.

The CLI bits were simply there from when os-ken was forked from Ryu
but we actually never used those tools in OpenStack so it is better
to remove them and keep os-ken as small as possible.

[1] https://meetings.opendev.org/meetings/networking/2025/networking.2025-02-25-14.00.log.html#l-81

Closes-bug: #2100268

Change-Id: I40a60a3233718fcda2f0be25120ef92708ffcbd5
Signed-off-by: Slawek Kaplonski <skaplons@redhat.com>
This commit is contained in:
Slawek Kaplonski
2025-08-29 15:58:06 +02:00
parent bf639392f3
commit d85897884d
14 changed files with 5 additions and 1412 deletions

View File

@@ -1,48 +0,0 @@
:orphan:
os_ken manual page
==================
Synopsis
--------
**osken** [-h] [--config-dir DIR] [--config-file PATH] [--version] [subcommand] ...
Description
-----------
:program:`os_ken` is the executable for OS-Ken applications. os_ken loads a sub-module
corresponding to the sub-command and run it. 'run' sub-command is an
equivalent to osken-manager.
OS-Ken is a component-based software defined networking framework. OS-Ken
provides software components with well defined API that make it easy for
developers to create new network management and control applications.
OS-Ken supports various protocols for managing network devices, such as
OpenFlow, Netconf, OF-config, etc. About OpenFlow, OS-Ken supports fully
1.0, 1.2, 1.3, 1.4 and Nicira Extensions.
Options
-------
subcommand
[rpc-cli|run|of-config-cli]
subcommand_args
subcommand specific arguments
-h, --help
show this help message and exit
--config-dir DIR
Path to a config directory to pull \*.conf files from.
This file set is sorted, so as to provide a predictable
parse order if individual options are over-ridden. The
set is parsed after the file(s) specified via previous
--config-file, arguments hence over-ridden options in
the directory take precedence.
--config-file PATH
Path to a config file to use. Multiple config files can
be specified, with values in later files taking
precedence. The default files used are: None
--version
show program's version number and exit

View File

@@ -1,184 +0,0 @@
:orphan:
osken-manager manual page
==========================
Synopsis
--------
**osken-manager** [-h]
[--app-lists APP_LISTS] [--ca-certs CA_CERTS]
[--config-dir DIR] [--config-file PATH]
[--ctl-cert CTL_CERT] [--ctl-privkey CTL_PRIVKEY]
[--default-log-level DEFAULT_LOG_LEVEL] [--explicit-drop]
[--install-lldp-flow] [--log-config-file LOG_CONFIG_FILE]
[--log-dir LOG_DIR] [--log-file LOG_FILE]
[--log-file-mode LOG_FILE_MODE]
[--neutron-admin-auth-url NEUTRON_ADMIN_AUTH_URL]
[--neutron-admin-password NEUTRON_ADMIN_PASSWORD]
[--neutron-admin-tenant-name NEUTRON_ADMIN_TENANT_NAME]
[--neutron-admin-username NEUTRON_ADMIN_USERNAME]
[--neutron-auth-strategy NEUTRON_AUTH_STRATEGY]
[--neutron-controller-addr NEUTRON_CONTROLLER_ADDR]
[--neutron-url NEUTRON_URL]
[--neutron-url-timeout NEUTRON_URL_TIMEOUT]
[--noexplicit-drop] [--noinstall-lldp-flow]
[--noobserve-links] [--nouse-stderr] [--nouse-syslog]
[--noverbose] [--observe-links]
[--ofp-listen-host OFP_LISTEN_HOST]
[--ofp-ssl-listen-port OFP_SSL_LISTEN_PORT]
[--ofp-tcp-listen-port OFP_TCP_LISTEN_PORT] [--use-stderr]
[--use-syslog] [--verbose] [--version]
[--wsapi-host WSAPI_HOST] [--wsapi-port WSAPI_PORT]
[--test-switch-dir TEST-SWITCH_DIR]
[--test-switch-target TEST-SWITCH_TARGET]
[--test-switch-tester TEST-SWITCH_TESTER]
[app [app ...]]
Description
-----------
:program:`osken-manager` is the executable for OS-Ken applications. osken-manager
loads OS-Ken applications and run it.
OS-Ken is a component-based software defined networking framework. OS-Ken
provides software components with well defined API that make it easy for
developers to create new network management and control applications.
OS-Ken supports various protocols for managing network devices, such as
OpenFlow, Netconf, OF-config, etc. About OpenFlow, OS-Ken supports fully
1.0, 1.2, 1.3, 1.4 and Nicira Extensions.
Options
-------
app
application module name to run
-h, --help
show this help message and exit
--app-lists APP_LISTS
application module name to run
--ca-certs CA_CERTS
CA certificates
--config-dir DIR
Path to a config directory to pull \*.conf files from.
This file set is sorted, so as to provide a
predictable parse order if individual options are
over-ridden. The set is parsed after the file(s)
specified via previous --config-file, arguments hence
over-ridden options in the directory take precedence.
--config-file PATH
Path to a config file to use. Multiple config files
can be specified, with values in later files taking
precedence. The default files used are: None
--ctl-cert CTL_CERT
controller certificate
--ctl-privkey CTL_PRIVKEY
controller private key
--default-log-level DEFAULT_LOG_LEVEL
default log level
--explicit-drop
link discovery: explicitly drop lldp packet in
--install-lldp-flow
link discovery: explicitly install flow entry to send
lldp packet to controller
--log-config-file LOG_CONFIG_FILE
Path to a logging config file to use
--log-dir LOG_DIR
log file directory
--log-file LOG_FILE
log file name
--log-file-mode LOG_FILE_MODE
default log file permission
--neutron-admin-auth-url NEUTRON_ADMIN_AUTH_URL
auth url for connecting to neutron in admin context
--neutron-admin-password NEUTRON_ADMIN_PASSWORD
password for connecting to neutron in admin context
--neutron-admin-tenant-name NEUTRON_ADMIN_TENANT_NAME
tenant name for connecting to neutron in admin context
--neutron-admin-username NEUTRON_ADMIN_USERNAME
username for connecting to neutron in admin context
--neutron-auth-strategy NEUTRON_AUTH_STRATEGY
auth strategy for connecting to neutron in admincontext
--neutron-controller-addr NEUTRON_CONTROLLER_ADDR
openflow method:address:port to set controller ofovs bridge
--neutron-url NEUTRON_URL
URL for connecting to neutron
--neutron-url-timeout NEUTRON_URL_TIMEOUT
timeout value for connecting to neutron in seconds
--noexplicit-drop
The inverse of --explicit-drop
--noinstall-lldp-flow
The inverse of --install-lldp-flow
--noobserve-links
The inverse of --observe-links
--nouse-stderr
The inverse of --use-stderr
--nouse-syslog
The inverse of --use-syslog
--noverbose
The inverse of --verbose
--observe-links
observe link discovery events.
--ofp-listen-host OFP_LISTEN_HOST
openflow listen host
--ofp-ssl-listen-port OFP_SSL_LISTEN_PORT
openflow ssl listen port
--ofp-tcp-listen-port OFP_TCP_LISTEN_PORT
openflow tcp listen port
--use-stderr
log to standard error
--use-syslog
output to syslog
--verbose
show debug output
--version
show program's version number and exit
--wsapi-host WSAPI_HOST
webapp listen host
--wsapi-port WSAPI_PORT
webapp listen port
--test-switch-dir TEST-SWITCH_DIR
test files directory
--test-switch-target TEST-SWITCH_TARGET
target sw dp-id
--test-switch-tester TEST-SWITCH_TESTER
tester sw dp-id

View File

View File

@@ -1,112 +0,0 @@
#!/usr/bin/env python3
#
# Copyright (C) 2011, 2012 Nippon Telegraph and Telephone Corporation.
# Copyright (C) 2011 Isaku Yamahata <yamahata at valinux co jp>
#
# 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 os
import sys
from os_ken.lib import hub
hub.patch(thread=False)
from os_ken import cfg
import logging
from os_ken import log
log.early_init_log(logging.DEBUG)
from os_ken import flags
from os_ken import __version__ as version
from os_ken.base.app_manager import AppManager
from os_ken.controller import controller
from os_ken.topology import switches
CONF = cfg.CONF
CONF.register_cli_opts([
cfg.ListOpt('app-lists', default=[],
help='application module name to run'),
cfg.MultiStrOpt('app', positional=True, default=[],
help='application module name to run'),
cfg.StrOpt('pid-file', default=None, help='pid file name'),
cfg.BoolOpt('enable-debugger', default=False,
help='don\'t overwrite Python standard threading library'
'(use only for debugging)'),
cfg.StrOpt('user-flags', default=None,
help='Additional flags file for user applications'),
])
def _parse_user_flags():
"""
Parses user-flags file and loads it to register user defined options.
"""
try:
idx = list(sys.argv).index('--user-flags')
user_flags_file = sys.argv[idx + 1]
except (ValueError, IndexError):
user_flags_file = ''
if user_flags_file and os.path.isfile(user_flags_file):
from os_ken.utils import _import_module_file
_import_module_file(user_flags_file)
def main(args=None, prog=None):
_parse_user_flags()
try:
CONF(args=args, prog=prog,
project='os_ken', version='osken-manager %s' % version,
default_config_files=['/usr/local/etc/os_ken/os_ken.conf'])
except cfg.ConfigFilesNotFoundError:
CONF(args=args, prog=prog,
project='os_ken', version='osken-manager %s' % version)
log.init_log()
logger = logging.getLogger(__name__)
if CONF.enable_debugger:
msg = 'debugging is available (--enable-debugger option is turned on)'
logger.info(msg)
else:
hub.patch(thread=True)
if CONF.pid_file:
with open(CONF.pid_file, 'w') as pid_file:
pid_file.write(str(os.getpid()))
app_lists = CONF.app_lists + CONF.app
# keep old behavior, run ofp if no application is specified.
if not app_lists:
app_lists = ['os_ken.controller.ofp_handler']
app_mgr = AppManager.get_instance()
app_mgr.load_apps(app_lists)
contexts = app_mgr.create_contexts()
services = []
services.extend(app_mgr.instantiate_apps(**contexts))
try:
hub.joinall(services)
except KeyboardInterrupt:
logger.debug("Keyboard Interrupt received. "
"Closing OSKen application manager...")
finally:
app_mgr.close()
if __name__ == "__main__":
main()

View File

@@ -1,559 +0,0 @@
#!/usr/bin/env python3
#
# Copyright (C) 2013 Nippon Telegraph and Telephone Corporation.
# Copyright (C) 2013 YAMAMOTO Takashi <yamamoto at valinux co jp>
#
# 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.
# a simple command line OF-CONFIG client
#
# a usage example:
# % PYTHONPATH=. ./bin/of_config_cli \
# --peers=sw1=localhost:1830:username:password
# (Cmd) raw_get sw1
import cmd
import sys
import lxml.etree as ET
from ncclient.operations.rpc import RPCError
from os_ken import cfg
from os_ken.lib import of_config
from os_ken.lib.of_config import capable_switch
import os_ken.lib.of_config.classes as ofc
CONF = cfg.CONF
CONF.register_cli_opts([
cfg.ListOpt('peers', default=[], help='list of peers')
])
class Peer(capable_switch.OFCapableSwitch):
def __init__(self, name, host, port, username, password):
self._name = name
super(Peer, self).__init__(
host=host, port=port, username=username, password=password,
unknown_host_cb=lambda host, fingeprint: True)
peers = {}
def add_peer(name, host, port, username, password):
peers[name] = Peer(name, host, port, username, password)
def et_tostring_pp(tree):
# pretty_print is an lxml feature, not available in ElementTree
try:
return ET.tostring(tree, pretty_print=True)
except TypeError:
return ET.tostring(tree)
def validate(tree):
schema = ET.XMLSchema(file=of_config.OF_CONFIG_1_1_1_XSD)
if not schema(tree):
print(schema.error_log)
class Cmd(cmd.Cmd):
def __init__(self, *args, **kwargs):
self._in_onecmd = False
cmd.Cmd.__init__(self, *args, **kwargs)
def _request(self, line, f):
args = line.split()
try:
peer = args[0]
except:
print("argument error")
return
try:
p = peers[peer]
except KeyError:
print("unknown peer %s" % peer)
return
try:
f(p, args[1:])
except RPCError as e:
print("RPC Error %s" % e)
except EOFError:
print("disconnected")
def _complete_peer(self, text, line, _begidx, _endidx):
if len((line + 'x').split()) >= 3:
return []
return [name for name in peers if name.startswith(text)]
def do_list_cap(self, line):
"""list_cap <peer>
"""
def f(p, args):
for i in p.netconf.server_capabilities:
print(i)
self._request(line, f)
def do_raw_get(self, line):
"""raw_get <peer>
"""
def f(p, args):
result = p.raw_get()
tree = ET.fromstring(result)
validate(tree)
print(et_tostring_pp(tree))
self._request(line, f)
def do_raw_get_config(self, line):
"""raw_get_config <peer> <source>
"""
def f(p, args):
try:
source = args[0]
except:
print("argument error")
return
result = p.raw_get_config(source)
tree = ET.fromstring(result)
validate(tree)
print(et_tostring_pp(tree))
self._request(line, f)
def do_get(self, line):
"""get <peer>
eg. get sw1
"""
def f(p, args):
print(p.get())
self._request(line, f)
def do_commit(self, line):
"""commit <peer>
eg. commit sw1
"""
def f(p, args):
print(p.commit())
self._request(line, f)
def do_discard(self, line):
"""discard <peer>
eg. discard sw1
"""
def f(p, args):
print(p.discard_changes())
self._request(line, f)
def do_get_config(self, line):
"""get_config <peer> <source>
eg. get_config sw1 startup
"""
def f(p, args):
try:
source = args[0]
except:
print("argument error")
return
print(p.get_config(source))
self._request(line, f)
def do_delete_config(self, line):
"""delete_config <peer> <source>
eg. delete_config sw1 startup
"""
def f(p, args):
try:
source = args[0]
except:
print("argument error")
return
print(p.delete_config(source))
self._request(line, f)
def do_copy_config(self, line):
"""copy_config <peer> <source> <target>
eg. copy_config sw1 running startup
"""
def f(p, args):
try:
source, target = args
except:
print("argument error")
return
print(p.copy_config(source, target))
self._request(line, f)
def do_list_port(self, line):
"""list_port <peer>
"""
def f(p, args):
o = p.get()
for p in o.resources.port:
print('%s %s %s' % (p.resource_id, p.name, p.number))
self._request(line, f)
_port_settings = [
'admin-state',
'no-forward',
'no-packet-in',
'no-receive',
]
def do_get_port_config(self, line):
"""get_config_port <peer> <source> <port>
eg. get_port_config sw1 running LogicalSwitch7-Port2
"""
def f(p, args):
try:
source, port = args
except:
print("argument error")
return
o = p.get_config(source)
for p in o.resources.port:
if p.resource_id != port:
continue
print(p.resource_id)
conf = p.configuration
for k in self._port_settings:
try:
v = getattr(conf, k)
except AttributeError:
continue
print('%s %s' % (k, v))
self._request(line, f)
def do_set_port_config(self, line):
"""set_port_config <peer> <target> <port> <key> <value>
eg. set_port_config sw1 running LogicalSwitch7-Port2 admin-state down
eg. set_port_config sw1 running LogicalSwitch7-Port2 no-forward false
"""
def f(p, args):
try:
target, port, key, value = args
except:
print("argument error")
print(args)
return
# get switch id
o = p.get()
capable_switch_id = o.id
try:
capable_switch = ofc.OFCapableSwitchType(
id=capable_switch_id,
resources=ofc.OFCapableSwitchResourcesType(
port=[
ofc.OFPortType(
resource_id=port,
configuration=ofc.OFPortConfigurationType(
**{key: value}))
]
)
)
except TypeError:
print("argument error")
return
try:
p.edit_config(target, capable_switch)
except Exception as e:
print(e)
self._request(line, f)
def do_list_queue(self, line):
"""list_queue <peer>
"""
def f(p, args):
o = p.get()
if o.resources.queue:
for q in o.resources.queue:
print('%s %s' % (q.resource_id, q.port))
self._request(line, f)
_queue_settings = [
'max-rate',
'min-rate',
'experimenter',
]
def do_get_queue_config(self, line):
"""get_queue_port <peer> <source> <queue>
eg. get_queue_config sw1 running LogicalSwitch7-Port1-Queue922
"""
def f(p, args):
try:
source, queue = args
except:
print("argument error")
return
o = p.get_config(source)
for q in o.resources.queue:
if q.resource_id != queue:
continue
print(q.resource_id)
conf = q.properties
for k in self._queue_settings:
try:
v = getattr(conf, k)
except AttributeError:
continue
print('%s %s' % (k, v))
self._request(line, f)
def do_set_queue_config(self, line):
"""set_queue_config <peer> <target> <queue> <key> <value>
eg. set_queue_config sw1 running LogicalSwitch7-Port1-Queue922 \
max-rate 100
"""
def f(p, args):
try:
target, queue, key, value = args
except:
print("argument error")
print(args)
return
# get switch id
o = p.get()
capable_switch_id = o.id
try:
capable_switch = ofc.OFCapableSwitchType(
id=capable_switch_id,
resources=ofc.OFCapableSwitchResourcesType(
queue=[
ofc.OFQueueType(
resource_id=queue,
properties=ofc.OFQueuePropertiesType(
**{key: value})),
]
)
)
except TypeError:
print("argument error")
return
try:
p.edit_config(target, capable_switch)
except Exception as e:
print(e)
self._request(line, f)
def do_add_queue(self, line):
"""add_queue <peer> <target> <logical-switch> <queue>
eg. add_queue sw1 running LogicalSwitch7 NameOfNewQueue
"""
def f(p, args):
try:
target, lsw, queue = args
except:
print("argument error")
print(args)
return
# get switch id
o = p.get()
capable_switch_id = o.id
try:
capable_switch = ofc.OFCapableSwitchType(
id=capable_switch_id,
resources=ofc.OFCapableSwitchResourcesType(
queue=[
ofc.OFQueueType(resource_id=queue)
]
),
logical_switches=ofc.OFCapableSwitchLogicalSwitchesType(
switch=[ofc.OFLogicalSwitchType(
id=lsw,
resources=ofc.OFLogicalSwitchResourcesType(
queue=[queue])
)]
)
)
except TypeError:
print("argument error")
return
try:
p.edit_config(target, capable_switch)
except Exception as e:
print(e)
self._request(line, f)
def do_list_logical_switch(self, line):
"""list_logical_switch <peer>
"""
def f(p, args):
o = p.get()
for s in o.logical_switches.switch:
print('%s %s' % (s.id, s.datapath_id))
self._request(line, f)
def do_show_logical_switch(self, line):
"""show_logical_switch <peer> <logical switch>
"""
def f(p, args):
try:
(lsw,) = args
except:
print("argument error")
return
o = p.get()
for s in o.logical_switches.switch:
if s.id != lsw:
continue
print(s.id)
print('datapath-id %s' % s.datapath_id)
if s.resources.queue:
print('queues:')
for q in s.resources.queue:
print('\t %s' % q)
if s.resources.port:
print('ports:')
for p in s.resources.port:
print('\t %s' % p)
self._request(line, f)
_lsw_settings = [
'lost-connection-behavior',
]
def do_get_logical_switch_config(self, line):
"""get_logical_switch_config <peer> <source> <logical switch>
"""
def f(p, args):
try:
source, lsw = args
except:
print("argument error")
return
o = p.get_config(source)
for l in o.logical_switches.switch:
if l.id != lsw:
continue
print(l.id)
for k in self._lsw_settings:
try:
v = getattr(l, k)
except AttributeError:
continue
print('%s %s' % (k, v))
self._request(line, f)
def do_set_logical_switch_config(self, line):
"""set_logical_switch_config <peer> <logical switch> <key> <value>
eg. set_logical_switch_config sw1 running LogicalSwitch7 \
lost-connection-behavior failStandaloneMode
"""
def f(p, args):
try:
target, lsw, key, value = args
except:
print("argument error")
return
# get switch id
o = p.get_config(target)
capable_switch_id = o.id
try:
capable_switch = ofc.OFCapableSwitchType(
id=capable_switch_id,
logical_switches=ofc.OFCapableSwitchLogicalSwitchesType(
switch=[ofc.OFLogicalSwitchType(
id=lsw,
**{key: value}
)]
)
)
except TypeError:
print("argument error")
return
try:
p.edit_config(target, capable_switch)
except Exception as e:
print(e)
self._request(line, f)
completedefault = _complete_peer
def complete_EOF(self, _text, _line, _begidx, _endidx):
return []
def do_EOF(self, _line):
sys.exit(0)
def onecmd(self, string):
self._in_onecmd = True
try:
return cmd.Cmd.onecmd(self, string)
finally:
self._in_onecmd = False
def main(args=None, prog=None):
CONF(args=args, prog=prog,
project='of-config-cli', version='of-config-cli')
for p_str in CONF.peers:
name, addr = p_str.split('=')
host, port, username, password = addr.rsplit(':', 3)
add_peer(name, host, port, username, password)
Cmd().cmdloop()
if __name__ == "__main__":
main()

View File

@@ -1,42 +0,0 @@
#!/usr/bin/env python3
# Copyright (C) 2014 VA Linux Systems Japan K.K.
# 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.
#
# @author: Fumihiko Kakuma, VA Linux Systems Japan K.K.
# @author: YAMAMOTO Takashi, VA Linux Systems Japan K.K.
# NOTE: This module is used by Neutron "ofagent" agent for
# IceHouse release. Juno and later releases do not use this.
# TODO: Remove this module when IceHouse is EOL'ed.
from os_ken.lib import hub
hub.patch()
from os_ken import cfg
from neutron.common import config as logging_config
from os_ken.base.app_manager import AppManager
def main():
cfg.CONF(project='os_ken')
logging_config.setup_logging(cfg.CONF)
AppManager.run_apps(['neutron.plugins.ofagent.agent.ofa_neutron_agent'])
if __name__ == "__main__":
main()

View File

@@ -1,74 +0,0 @@
# Copyright (C) 2014 Nippon Telegraph and Telephone Corporation.
# Copyright (C) 2014 YAMAMOTO Takashi <yamamoto at valinux co jp>
#
# 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 argparse
import os.path
import sys
from os_ken import cfg
from os_ken import utils
from os_ken import __version__
subcommands = {
'run': 'os_ken.cmd.manager',
'of-config-cli': 'os_ken.cmd.of_config_cli',
'rpc-cli': 'os_ken.cmd.rpc_cli',
}
class RemainderOpt(cfg.MultiStrOpt):
def _get_argparse_kwargs(self, group, **kwargs):
kwargs = cfg.MultiStrOpt._get_argparse_kwargs(self, group, **kwargs)
kwargs['nargs'] = argparse.REMAINDER
return kwargs
base_conf = cfg.ConfigOpts()
base_conf.register_cli_opt(cfg.StrOpt('subcommand', positional=True,
required=True,
help='[%s]' % '|'.join(
list(subcommands.keys()))))
base_conf.register_cli_opt(RemainderOpt('subcommand_args', default=[],
positional=True,
help='subcommand specific arguments'))
class SubCommand(object):
def __init__(self, name, entry):
self.name = name
self.entry = entry
def run(self, args):
prog = '%s %s' % (os.path.basename(sys.argv[0]), self.name,)
self.entry(args=args, prog=prog)
def main():
try:
base_conf(project='os_ken', version='os_ken %s' % __version__)
except cfg.RequiredOptError as e:
base_conf.print_help()
raise SystemExit(1)
subcmd_name = base_conf.subcommand
try:
subcmd_mod_name = subcommands[subcmd_name]
except KeyError:
base_conf.print_help()
raise SystemExit('Unknown subcommand %s' % subcmd_name)
subcmd_mod = utils.import_module(subcmd_mod_name)
subcmd = SubCommand(name=subcmd_name, entry=subcmd_mod.main)
subcmd.run(base_conf.subcommand_args)

View File

@@ -1,263 +0,0 @@
#!/usr/bin/env python3
#
# Copyright (C) 2013 Nippon Telegraph and Telephone Corporation.
# Copyright (C) 2013 YAMAMOTO Takashi <yamamoto at valinux co jp>
#
# 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.
# a simple command line msgpack-rpc client
#
# a usage example:
# % PYTHONPATH=. ./bin/rpc-cli \
# --peers=echo-server=localhost:9999,hoge=localhost:9998
# (Cmd) request echo-server echo ["hoge"]
# RESULT hoge
# (Cmd) request echo-server notify ["notify-method", ["param1","param2"]]
# RESULT notify-method
# (Cmd)
# NOTIFICATION from echo-server ['notify-method', ['param1', 'param2']]
# (Cmd)
import ast
import cmd
import signal
import socket
import sys
import termios
from os_ken import cfg
from os_ken.lib import rpc
CONF = cfg.CONF
CONF.register_cli_opts([
cfg.ListOpt('peers', default=[],
help='List of peers, separated by commas. '
'(e.g., "hoge=localhost:9998,fuga=localhost:9999")'),
cfg.StrOpt('command', short='c', default=None,
help='Command to be executed as single command. '
'The default is None and opens interactive console.'),
])
class Peer(object):
def __init__(self, name, addr):
self._name = name
self._addr = addr
self.socket = None
self.client = None
try:
self.connect()
except ConnectionError as e:
print('Exception when connecting to peer "%s": %s' % (name, e))
raise e
def connect(self):
self.socket = socket.create_connection(self._addr)
self.client = rpc.Client(self.socket,
notification_callback=self.notification)
def try_to_connect(self, verbose=False):
if self.client:
return
try:
self.connect()
assert self.client
except Exception as e:
if verbose:
print("connection failure %s" % e)
raise EOFError
def notification(self, n):
print("NOTIFICATION from %s %s" % (self._name, n))
def call(self, method, params):
return self._do(lambda: self.client.call(method, params))
def send_notification(self, method, params):
self._do(lambda: self.client.send_notification(method, params))
def _do(self, f):
def g():
try:
return f()
except EOFError:
self.client = None
raise
self.try_to_connect(verbose=True)
try:
return g()
except EOFError:
print("disconnected. trying to connect...")
self.try_to_connect(verbose=True)
print("connected. retrying the request...")
return g()
def close(self):
self.socket.close()
peers = {}
def add_peer(name, host, port):
try:
peer = Peer(name, (host, port))
except ConnectionError:
return
peers[name] = peer
def close_peers():
for peer in peers.values():
peer.socket.close()
class Cmd(cmd.Cmd):
def __init__(self, *args, **kwargs):
self._in_onecmd = False
self._notification_check_interval = 1 # worth to be configurable?
self._saved_termios = None
cmd.Cmd.__init__(self, *args, **kwargs)
def _request(self, line, f):
args = line.split(None, 2)
try:
peer = args[0]
method = args[1]
params = ast.literal_eval(args[2])
except (IndexError, ValueError) as e:
print("argument error: %s" % e)
return
try:
p = peers[peer]
except KeyError:
print("unknown peer %s" % peer)
return
try:
f(p, method, params)
except rpc.RPCError as e:
print("RPC ERROR %s" % e)
except EOFError:
print("disconnected")
def _complete_peer(self, text, line, _begidx, _endidx):
if len((line + 'x').split()) >= 3:
return []
return [name for name in peers if name.startswith(text)]
def do_request(self, line):
"""request <peer> <method> <params>
send a msgpack-rpc request and print a response.
<params> is a python code snippet, it should be eval'ed to a list.
"""
def f(p, method, params):
result = p.call(method, params)
print("RESULT %s" % result)
self._request(line, f)
def do_notify(self, line):
"""notify <peer> <method> <params>
send a msgpack-rpc notification.
<params> is a python code snippet, it should be eval'ed to a list.
"""
def f(p, method, params):
p.send_notification(method, params)
self._request(line, f)
def complete_request(self, text, line, begidx, endidx):
return self._complete_peer(text, line, begidx, endidx)
def complete_notify(self, text, line, begidx, endidx):
return self._complete_peer(text, line, begidx, endidx)
def do_EOF(self, _line=None):
close_peers()
sys.exit(0)
def emptyline(self):
self._peek_notification()
def postcmd(self, _stop, _line):
self._peek_notification()
def _peek_notification(self):
for k, p in peers.items():
if p.client:
try:
p.client.peek_notification()
except EOFError:
p.client = None
print("disconnected %s" % k)
@staticmethod
def _save_termios():
return termios.tcgetattr(sys.stdin.fileno())
@staticmethod
def _restore_termios(t):
termios.tcsetattr(sys.stdin.fileno(), termios.TCSADRAIN, t)
def preloop(self):
self._saved_termios = self._save_termios()
signal.signal(signal.SIGALRM, self._timeout)
signal.alarm(1)
def postloop(self):
close_peers()
def onecmd(self, string):
self._in_onecmd = True
try:
return cmd.Cmd.onecmd(self, string)
finally:
self._in_onecmd = False
def _timeout(self, _sig, _frame):
if not self._in_onecmd:
# restore terminal settings. (cooked/raw, ...)
# required for pypy at least.
# this doesn't seem to be needed for cpython readline
# module but i'm not sure if it's by spec or luck.
o = self._save_termios()
self._restore_termios(self._saved_termios)
self._peek_notification()
self._restore_termios(o)
signal.alarm(self._notification_check_interval)
def main(args=None, prog=None):
CONF(args=args, prog=prog, project='rpc-cli', version='rpc-cli')
for p_str in CONF.peers:
name, addr = p_str.split('=')
host, port = addr.rsplit(':', 1)
add_peer(name, host, port)
if CONF.command:
command = Cmd()
command.onecmd(CONF.command)
command.do_EOF()
Cmd().cmdloop()
if __name__ == "__main__":
main()

View File

@@ -1,15 +0,0 @@
# Copyright (C) 2013 Nippon Telegraph and Telephone Corporation.
# Copyright (C) 2013 YAMAMOTO Takashi <yamamoto at valinux co jp>
#
# 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

@@ -1,21 +0,0 @@
# Copyright (C) 2013 Nippon Telegraph and Telephone Corporation.
# Copyright (C) 2013 YAMAMOTO Takashi <yamamoto at valinux co jp>
#
# 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 os_ken.base import app_manager
class DummyApp(app_manager.OSKenApp):
pass

View File

@@ -1,22 +0,0 @@
# Copyright (C) 2013,2014 Nippon Telegraph and Telephone Corporation.
# Copyright (C) 2013,2014 YAMAMOTO Takashi <yamamoto at valinux co jp>
#
# 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 os_ken.base import app_manager
from os_ken.ofproto import ofproto_v1_3
class DummyOpenFlowApp(app_manager.OSKenApp):
OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION]

View File

@@ -1,68 +0,0 @@
# Copyright (C) 2013,2014 Nippon Telegraph and Telephone Corporation.
# Copyright (C) 2013,2014 YAMAMOTO Takashi <yamamoto at valinux co jp>
#
# 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 unittest
from unittest import mock
from importlib import reload
from os_ken.cmd.manager import main
class Test_Manager(unittest.TestCase):
"""Test osken-manager command
"""
def __init__(self, methodName):
super(Test_Manager, self).__init__(methodName)
def setUp(self):
pass
def tearDown(self):
pass
@mock.patch('sys.argv', new=['osken-manager', '--version'])
def test_version(self):
self.assertRaises(SystemExit, main)
@mock.patch('sys.argv', new=['osken-manager', '--help'])
def test_help(self):
self.assertRaises(SystemExit, main)
@staticmethod
def _reset_globals():
# hack to reset globals like SERVICE_BRICKS.
# assumption: this is the only test which actually starts OSKenApp.
import os_ken.base.app_manager
import os_ken.ofproto.ofproto_protocol
reload(os_ken.base.app_manager)
reload(os_ken.ofproto.ofproto_protocol)
@mock.patch('sys.argv', new=['osken-manager', '--verbose',
'os_ken.tests.unit.cmd.dummy_app'])
def test_no_services(self):
self._reset_globals()
main()
self._reset_globals()
@mock.patch('sys.argv', new=['osken-manager', '--verbose',
'os_ken.tests.unit.cmd.dummy_openflow_app'])
def test_openflow_app(self):
self._reset_globals()
main()
self._reset_globals()

View File

@@ -0,0 +1,5 @@
---
other:
- |
CLI tools `osken` and `osken-manager` are removed completely.

View File

@@ -24,7 +24,3 @@ classifier =
packages = packages =
os_ken os_ken
[entry_points]
console_scripts =
osken = os_ken.cmd.osken_base:main
osken-manager = os_ken.cmd.manager:main