rack command uses the cliff framework

Change-Id: I50edf6be8b59ceac39e66cb12cd1048a03230237
This commit is contained in:
takehirokaneko 2015-03-11 16:44:09 +09:00
parent fa207dd7e6
commit 7392bbe2ed
36 changed files with 2046 additions and 1964 deletions

View File

@ -1,4 +1,5 @@
[DEFAULT]
test_command=${PYTHON:-python} -m subunit.run discover -t ./ ./ $LISTOPT $IDOPTION
test_command=RACK_IS_TEST=1 \
${PYTHON:-python} -m subunit.run discover -t ./ ./ $LISTOPT $IDOPTION
test_id_option=--load-list $IDFILE
test_list_option=--list

View File

@ -59,10 +59,3 @@ glance_image_id =
### Not required params ###
#name =
# Userdata file path like /path/to/file
#userdata =
# Key-value pairs to be passed to metadata server
# Syntax: args = key1=value1,key2=value2,...
#args =

View File

@ -146,3 +146,26 @@ class InvalidProcessError(BaseError):
class ProcessInitError(BaseError):
pass
class EndOfFile(Exception):
message = 'EOF'
class NoDescriptor(Exception):
message = 'Descriptor Not Found'
def __init__(self, message=None):
self.message = message or self.__class__.message
def __str__(self):
formatted_string = self.message
return formatted_string
class NoReadDescriptor(NoDescriptor):
message = 'Read Descriptor Not Found'
class NoWriteDescriptor(NoDescriptor):
message = 'Write Descriptor Not Found'

107
rackclient/lib/__init__.py Normal file
View File

@ -0,0 +1,107 @@
from rackclient import exceptions
from rackclient.client import Client
import cPickle
import json
import logging
import os
import pika
import requests
LOG = logging.getLogger(__name__)
META_URL = 'http://169.254.169.254/openstack/latest/meta_data.json'
RACK_CTX = None
def get_rack_context(proxy_port=8088, client_version="1", api_version="v1"):
global RACK_CTX
if not RACK_CTX:
try:
resp = requests.get(META_URL)
metadata = json.loads(resp.text)["meta"]
proxy_url = 'http://%s:%d/%s' % (metadata["proxy_ip"], proxy_port, api_version)
client = Client(client_version, rack_url=proxy_url)
proxy_info = client.proxy.get(metadata["gid"])
RACK_CTX = type('', (object,), metadata)
RACK_CTX.ppid = getattr(RACK_CTX, "ppid", "")
RACK_CTX.client = client
RACK_CTX.fs_endpoint = proxy_info.fs_endpoint
RACK_CTX.ipc_endpoint = proxy_info.ipc_endpoint
RACK_CTX.shm_endpoint = proxy_info.shm_endpoint
try:
# Check if this process is not recognized by RACK.
RACK_CTX.client.processes.get(RACK_CTX.gid, RACK_CTX.pid)
except exceptions.NotFound:
msg = "This process is not recognized by RACK"
raise exceptions.InvalidProcessError(msg)
if RACK_CTX.ppid:
LOG.debug("Messaging: send message to %s", RACK_CTX.ppid)
msg = _Messaging(RACK_CTX)
msg.send_msg(RACK_CTX.ppid)
while True:
receive_msg = msg.receive_msg(getattr(RACK_CTX, "msg_limit_time", 180))
if receive_msg and RACK_CTX.ppid == receive_msg.get("pid"):
LOG.debug("Messaging: receive message from %s", receive_msg.get("pid"))
break
except Exception as e:
msg = "Failed to initialize the process: %s." % e.message
raise exceptions.ProcessInitError(msg)
return RACK_CTX
class _Messaging(object):
def __init__(self, ctx):
self.ctx = ctx
connection_param = pika.ConnectionParameters(self.ctx.proxy_ip)
if self.ctx.ipc_endpoint:
connection_param = pika.ConnectionParameters(self.ctx.ipc_endpoint)
self.connection = pika.BlockingConnection(connection_param)
self.channel = self.connection.channel()
self.channel.exchange_declare(exchange=ctx.gid, type='topic')
self.channel.queue_declare(queue=ctx.pid)
self.channel.queue_bind(exchange=ctx.gid, queue=ctx.pid, routing_key=ctx.gid + '.' + ctx.pid)
def receive_msg(self, timeout_limit=180):
queue_name = self.ctx.pid
self.channel = self.connection.channel()
receive = self.Receive()
self.connection.add_timeout(deadline=int(timeout_limit), callback_method=receive.time_out)
self.channel.basic_consume(receive.get_msg, queue=queue_name, no_ack=False)
receive.channel = self.channel
self.channel.start_consuming()
return receive.message
class Receive(object):
def __init__(self):
self.channel = None
self.message = None
def get_msg(self, ch, method, properties, body):
self.message = cPickle.loads(body)
ch.basic_ack(delivery_tag=method.delivery_tag)
ch.stop_consuming()
def time_out(self):
self.channel.stop_consuming()
def send_msg(self, target):
routing_key = self.ctx.gid + '.' + target
send_dict = {'pid': self.ctx.pid}
send_msg = cPickle.dumps(send_dict)
self.channel.basic_publish(exchange=self.ctx.gid, routing_key=routing_key, body=send_msg)
if not os.environ.get("RACK_IS_TEST"):
get_rack_context()

View File

@ -1,22 +1,23 @@
from rackclient.lib import RACK_CTX
from rackclient import exceptions
from swiftclient import client as swift_client
from swiftclient import exceptions as swift_exc
import json
import logging
import tempfile
from swiftclient import client as swift_client
from swiftclient import exceptions as swift_exc
from rackclient import process_context
from rackclient import exceptions
LOG = logging.getLogger(__name__)
PCTXT = process_context.PCTXT
SWIFT_PORT = 8080
def _get_swift_client():
if PCTXT.fs_endpoint:
if RACK_CTX.fs_endpoint:
try:
d = json.loads(PCTXT.fs_endpoint)
d = json.loads(RACK_CTX.fs_endpoint)
credentials = {
"user": d["os_username"],
"key": d["os_password"],
@ -29,7 +30,7 @@ def _get_swift_client():
msg = "The format of fs_endpoint is invalid."
raise exceptions.InvalidFSEndpointError(msg)
else:
authurl = "http://%s:%d/auth/v1.0" % (PCTXT.proxy_ip, SWIFT_PORT)
authurl = "http://%s:%d/auth/v1.0" % (RACK_CTX.proxy_ip, SWIFT_PORT)
credentials = {
"user": "rack:admin",
@ -38,6 +39,7 @@ def _get_swift_client():
}
swift = swift_client.Connection(**credentials)
authurl, token = swift.get_auth()
return swift_client.Connection(preauthurl=authurl, preauthtoken=token)
@ -138,4 +140,4 @@ class File(object):
return getattr(self.file, name)
else:
raise AttributeError("%s instance has no attribute '%s'",
self.__class__.__name__, name)
self.__class__.__name__, name)

View File

@ -1,31 +1,33 @@
from rackclient.lib import RACK_CTX
from rackclient import exceptions
import cPickle
import logging
import pika
from rackclient import exceptions
from rackclient import process_context
LOG = logging.getLogger(__name__)
PCTXT = process_context.PCTXT
class Messaging(object):
def __init__(self):
self.connection = _create_connection()
self.connection = self._create_connection()
self.channel = self.connection.channel()
self.declare_queue(PCTXT.pid)
self.declare_queue(RACK_CTX.pid)
def declare_queue(self, queue_name):
queue_name = str(queue_name)
self.channel.exchange_declare(exchange=PCTXT.gid, type='topic')
self.channel.exchange_declare(exchange=RACK_CTX.gid, type='topic')
self.channel.queue_declare(queue=queue_name)
self.channel.queue_bind(exchange=PCTXT.gid,
self.channel.queue_bind(exchange=RACK_CTX.gid,
queue=queue_name,
routing_key=PCTXT.gid + '.' + queue_name)
routing_key=RACK_CTX.gid + '.' + queue_name)
def receive_all_msg(self, queue_name=None,
timeout_limit=180, msg_limit_count=None):
if not queue_name:
queue_name = PCTXT.pid
queue_name = RACK_CTX.pid
self.channel = self.connection.channel()
receive = self.Receive()
@ -42,7 +44,7 @@ class Messaging(object):
def receive_msg(self, queue_name=None, timeout_limit=180):
if not queue_name:
queue_name = PCTXT.pid
queue_name = RACK_CTX.pid
self.channel = self.connection.channel()
receive = self.Receive()
timeout_limit = int(timeout_limit)
@ -79,36 +81,22 @@ class Messaging(object):
self.channel.stop_consuming()
def send_msg(self, target, message=None):
routing_key = PCTXT.gid + '.' + target
send_dict = {'pid': PCTXT.pid}
routing_key = RACK_CTX.gid + '.' + target
send_dict = {'pid': RACK_CTX.pid}
if message:
send_dict['message'] = message
send_msg = cPickle.dumps(send_dict)
self.channel.basic_publish(exchange=PCTXT.gid,
self.channel.basic_publish(exchange=RACK_CTX.gid,
routing_key=routing_key,
body=send_msg)
def _create_connection():
if PCTXT.ipc_endpoint:
connection_param = pika.ConnectionParameters(PCTXT.ipc_endpoint)
else:
connection_param = pika.ConnectionParameters(PCTXT.proxy_ip)
try:
connection = pika.BlockingConnection(connection_param)
except pika.exceptions.AMQPConnectionError as e:
raise exceptions.AMQPConnectionError(e)
return connection
def init():
msg = Messaging()
if PCTXT.ppid:
LOG.debug("Messaging: send message to %s", PCTXT.ppid)
msg.send_msg(PCTXT.ppid)
while True:
receive_msg = msg.receive_msg()
if receive_msg and PCTXT.ppid == receive_msg.get("pid"):
LOG.debug("Messaging: receive message from %s",
receive_msg.get("pid"))
break
def _create_connection(self):
if RACK_CTX.ipc_endpoint:
connection_param = pika.ConnectionParameters(RACK_CTX.ipc_endpoint)
else:
connection_param = pika.ConnectionParameters(RACK_CTX.proxy_ip)
try:
connection = pika.BlockingConnection(connection_param)
except pika.exceptions.AMQPConnectionError as e:
raise exceptions.AMQPConnectionError(e)
return connection

View File

@ -1,47 +1,17 @@
import logging
import time
from datetime import datetime
from rackclient.lib import RACK_CTX
from rackclient import exceptions
import logging
import redis
from rackclient import process_context
import time
LOG = logging.getLogger(__name__)
PCTXT = process_context.PCTXT
class EndOfFile(Exception):
message = 'EOF'
class NoDescriptor(Exception):
message = 'Descriptor Not Found'
def __init__(self, message=None):
self.message = message or self.__class__.message
def __str__(self):
formatted_string = self.message
return formatted_string
class NoReadDescriptor(NoDescriptor):
message = 'Read Descriptor Not Found'
class NoWriteDescriptor(NoDescriptor):
message = 'Write Descriptor Not Found'
def eof():
raise EndOfFile()
def no_reader():
raise NoReadDescriptor()
def no_writer():
raise NoWriteDescriptor()
PIPE = 1
FIFO = 2
PORT = 6379
def read_state_key(name):
@ -56,16 +26,13 @@ def reference_key_pattern(name="*", pid="*"):
return name + ":" + pid
PIPE = 1
FIFO = 2
PORT = 6379
class Pipe:
def __init__(self, name=None, read=None, write=None):
now = datetime.now()
self.host = PCTXT.proxy_ip
self.host = RACK_CTX.proxy_ip
self.port = PORT
if name:
self.is_named = True
self.r = redis.StrictRedis(host=self.host, port=self.port, db=FIFO)
@ -75,13 +42,13 @@ class Pipe:
else:
self.is_named = False
self.r = redis.StrictRedis(host=self.host, port=self.port, db=PIPE)
parent_pipe = self.r.keys(reference_key_pattern(pid=PCTXT.pid))
parent_pipe = self.r.keys(reference_key_pattern(pid=RACK_CTX.pid))
if parent_pipe:
self.name = self.r.get(parent_pipe[0])
else:
self.name = PCTXT.pid
read_state = self.r.hget(read_state_key(self.name), PCTXT.pid) or now
write_state = self.r.hget(write_state_key(self.name), PCTXT.pid) or now
self.name = RACK_CTX.pid
read_state = self.r.hget(read_state_key(self.name), RACK_CTX.pid) or now
write_state = self.r.hget(write_state_key(self.name), RACK_CTX.pid) or now
if read is not None:
if read:
read_state = now
@ -94,12 +61,12 @@ class Pipe:
write_state = "close"
self.read_state = read_state
self.write_state = write_state
self.r.hset(read_state_key(self.name), PCTXT.pid, self.read_state)
self.r.hset(write_state_key(self.name), PCTXT.pid, self.write_state)
self.r.hset(read_state_key(self.name), RACK_CTX.pid, self.read_state)
self.r.hset(write_state_key(self.name), RACK_CTX.pid, self.write_state)
def read(self):
if self.read_state == "close":
no_reader()
raise exceptions.NoReadDescriptor()
data = self._read()
while data is None:
data = self._read()
@ -110,26 +77,26 @@ class Pipe:
data = self.r.lpop(self.name)
if data is None and not self.has_writer():
self.flush()
eof()
raise exceptions.EndOfFile()
else:
return data
def write(self, data):
if self.write_state == "close":
no_writer()
raise exceptions.NoWriteDescriptor()
self.r.rpush(self.name, data)
if self.has_reader():
return True
else:
no_reader()
raise exceptions.NoReadDescriptor()
def close_reader(self):
self.read_state = "close"
self.r.hset(read_state_key(self.name), PCTXT.pid, self.read_state)
self.r.hset(read_state_key(self.name), RACK_CTX.pid, self.read_state)
def close_writer(self):
self.write_state = "close"
self.r.hset(write_state_key(self.name), PCTXT.pid, self.write_state)
self.r.hset(write_state_key(self.name), RACK_CTX.pid, self.write_state)
def has_reader(self):
read_states = self.r.hvals(read_state_key(self.name))
@ -158,9 +125,9 @@ class Pipe:
self.r.delete(*tuple(keys))
@classmethod
def flush_by_pid(cls, pid, host=None):
def flush_by_pid(self, pid, host=None):
if not host:
host = PCTXT.proxy_ip
host = RACK_CTX.proxy_ip
r = redis.StrictRedis(host=host, port=PORT, db=PIPE)
keys = [pid,
@ -172,7 +139,7 @@ class Pipe:
@classmethod
def flush_by_name(cls, name, host=None):
if not host:
host = PCTXT.proxy_ip
host = RACK_CTX.proxy_ip
r = redis.StrictRedis(host=host, port=PORT, db=FIFO)
keys = [name,
@ -183,8 +150,8 @@ class Pipe:
@classmethod
def share(cls, ppid, pid, host=None):
if not host:
host = PCTXT.proxy_ip
host = RACK_CTX.proxy_ip
now = datetime.now()
r = redis.StrictRedis(host=host, port=PORT, db=PIPE)
keys = r.keys(reference_key_pattern(pid=ppid))
@ -205,4 +172,4 @@ class Pipe:
current_write_state = now
r.hset(read_state_key(name), pid, current_read_state)
r.hset(write_state_key(name), pid, current_write_state)
return True
return True

View File

@ -1,7 +1,8 @@
import logging
import redis
from rackclient import process_context
from rackclient.lib import RACK_CTX
LOG = logging.getLogger(__name__)
@ -10,7 +11,7 @@ FIFO = 3
PORT = 6379
def get_host():
return process_context.PCTXT.proxy_ip
return RACK_CTX.proxy_ip
class Shm(object):

View File

@ -1,10 +1,10 @@
import websocket
import logging
from rackclient import process_context
import websocket
from rackclient.lib import RACK_CTX
LOG = logging.getLogger(__name__)
PCTXT = process_context.PCTXT
WS_PORT = 8888
@ -13,14 +13,14 @@ class SignalManager(object):
if url:
self.url = url.rstrip('/')
else:
self.url = "ws://" + ':'.join([PCTXT.proxy_ip, str(WS_PORT)])
self.url = "ws://" + ':'.join([RACK_CTX.proxy_ip, str(WS_PORT)])
def receive(self, on_msg_func, pid=None):
self.on_msg_func = on_msg_func
if pid:
header = 'PID: ' + pid
elif getattr(PCTXT, 'pid', False):
header = 'PID: ' + PCTXT.pid
elif getattr(RACK_CTX, 'pid', False):
header = 'PID: ' + RACK_CTX.pid
else:
raise Exception("Target PID is required.")
wsapp = websocket.WebSocketApp(

View File

@ -3,14 +3,14 @@ import Queue
import threading
from rackclient import exceptions
from rackclient import process_context
from rackclient.v1 import processes
from rackclient.v1.syscall.default import messaging
from rackclient.v1.syscall.default import pipe as rackpipe
from rackclient.v1.syscall.default import file as rackfile
from rackclient.lib import RACK_CTX
from rackclient.lib.syscall.default import messaging
from rackclient.lib.syscall.default import pipe as rackpipe
from rackclient.lib.syscall.default import file as rackfile
LOG = logging.getLogger(__name__)
PCTXT = process_context.PCTXT
def fork(opt_list, timeout_limit=180):
@ -20,8 +20,8 @@ def fork(opt_list, timeout_limit=180):
return_process_list = []
while True:
try:
child_list = _bulk_fork(PCTXT.pid, opt_list)
success_list, error_list = _check_connection(PCTXT.pid,
child_list = _bulk_fork(RACK_CTX.pid, opt_list)
success_list, error_list = _check_connection(RACK_CTX.pid,
child_list,
timeout_limit)
except Exception as e:
@ -42,29 +42,39 @@ def fork(opt_list, timeout_limit=180):
return return_process_list
def kill(pid):
RACK_CTX.client.processes.delete(RACK_CTX.gid, pid)
def pipe(name=None):
p = rackpipe.Pipe(name)
return p
def pipe_reader(name=None):
p = rackpipe.Pipe(name)
p.close_writer()
return p
def pipe_writer(name=None):
p = rackpipe.Pipe(name)
p.close_reader()
return p
def fopen(file_path, mode="r"):
return rackfile.File(file_path, mode)
def _bulk_fork(pid, args_list):
LOG.debug("start bulk_fork")
q = Queue.Queue()
def _fork(pid, **kwargs):
try:
child = PCTXT.client.processes.create(gid=PCTXT.gid,
child = RACK_CTX.client.processes.create(gid=RACK_CTX.gid,
ppid=pid,
**kwargs)
q.put(child)
except Exception as e:
attr = dict(args=kwargs, error=e)
q.put(processes.Process(PCTXT.client, attr))
q.put(processes.Process(RACK_CTX.client, attr))
tg = []
process_list = []
@ -116,7 +126,7 @@ def _check_connection(pid, process_list, timeout):
actives.append(process)
pid_list.remove(process.pid)
else:
PCTXT.client.processes.delete(PCTXT.gid, process.pid)
RACK_CTX.client.processes.delete(RACK_CTX.gid, process.pid)
inactives.append(process)
LOG.debug("_check_connection active processes count: %s", len(actives))

View File

@ -1,73 +0,0 @@
# Copyright (c) 2014 ITOCHU Techno-Solutions Corporation.
#
# 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 json
import requests
from rackclient import client
from rackclient.exceptions import MetadataAccessError
METADATA_URL = 'http://169.254.169.254/openstack/latest/meta_data.json'
class ProcessContext(object):
def __init__(self):
self.gid = None
self.pid = None
self.ppid = None
self.proxy_ip = None
self.proxy_url = None
self.fs_endpoint = None
self.shm_endpoint = None
self.ipc_endpoint = None
self.client = None
def add_args(self, args):
if isinstance(args, dict):
for (k, v) in args.items():
try:
setattr(self, k, v)
except AttributeError:
pass
PCTXT = ProcessContext()
def _get_metadata(metadata_url):
try:
resp = requests.get(metadata_url)
except Exception as e:
msg = "Could not get the metadata: %s" % e.message
raise MetadataAccessError(msg)
body = json.loads(resp.text)
return body['meta']
def init(client_version='1', proxy_port=8088, api_version='v1'):
metadata = _get_metadata(METADATA_URL)
PCTXT.gid = metadata.pop('gid')
PCTXT.pid = metadata.pop('pid')
PCTXT.ppid = metadata.pop('ppid') if 'ppid' in metadata else None
PCTXT.proxy_ip = metadata.pop('proxy_ip')
PCTXT.proxy_url = 'http://%s:%d/%s' % \
(PCTXT.proxy_ip, proxy_port, api_version)
PCTXT.client = client.Client(client_version, rack_url=PCTXT.proxy_url)
PCTXT.add_args(metadata)
proxy_info = PCTXT.client.proxy.get(PCTXT.gid)
PCTXT.fs_endpoint = proxy_info.fs_endpoint
PCTXT.shm_endpoint = proxy_info.shm_endpoint
PCTXT.ipc_endpoint = proxy_info.ipc_endpoint

View File

@ -1,201 +1,93 @@
# Copyright (c) 2014 ITOCHU Techno-Solutions Corporation.
#
# 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 the RACK API.
"""
import sys
import argparse
import logging
from oslo.utils import encodeutils
from rackclient.openstack.common.gettextutils import _
from rackclient.openstack.common import cliutils
from rackclient.v1 import shell as shell_v1
from rackclient import client
import os
import sys
from cliff.app import App
from cliff.commandmanager import CommandManager
import requests
from rackclient import exceptions
DEFAULT_RACK_API_VERSION = "1"
logger = logging.getLogger(__name__)
VERSION='1'
class RackShell(object):
class RackShell(App):
def get_base_parser(self):
parser = argparse.ArgumentParser(
prog='rack',
description=__doc__.strip(),
epilog='See "rack help COMMAND" '
'for help on a specific command.',
add_help=False,
log = logging.getLogger(__name__)
def __init__(self):
super(RackShell, self).__init__(
description='rack shell',
version=VERSION,
command_manager=CommandManager('rack.command'),
)
parser.add_argument(
'-h', '--help',
action='store_true',
help=argparse.SUPPRESS
)
parser.add_argument(
'--version',
action='version'
)
parser.add_argument(
'-d', '--debug',
default=False,
action='store_true',
help=_("Print debugging output")
)
def build_option_parser(self, description, version,
argparse_kwargs=None):
parser = super(RackShell, self).build_option_parser(
description, version, argparse_kwargs)
parser.add_argument(
'--rack-api-version',
metavar='<rack-api-ver>',
default=cliutils.env('RACK_API_VERSION',
default=DEFAULT_RACK_API_VERSION),
help=_('Accepts only 1, '
'defaults to env[RACK_API_VERSION].'))
metavar='<api-verion>',
default=os.environ.get('RACK_API_VERSION', VERSION),
help=('Accepts only 1, '
'defaults to env[RACK_API_VERSION].')
)
parser.add_argument(
'--rack-url',
metavar='<rack-url>',
default=cliutils.env('RACK_URL'),
help=_('Defaults to env[RACK_URL].'))
default=os.environ.get('RACK_URL', ''),
help='Defaults to env[RACK_URL].'
)
parser.add_argument(
'--gid',
metavar='<gid>',
default=cliutils.env('RACK_GID'),
help=_('Defaults to env[RACK_GID].'))
default=os.environ.get('RACK_GID', ''),
help='Defaults to env[RACK_GID].'
)
return parser
def get_subcommand_parser(self, version):
parser = self.get_base_parser()
def configure_logging(self):
super(RackShell, self).configure_logging()
self.subcommands = {}
subparsers = parser.add_subparsers(metavar='<subcommand>')
rlogger = logging.getLogger(requests.__name__)
rlogger.setLevel(logging.WARNING)
try:
actions_module = {
'1': shell_v1,
}[version]
except KeyError:
actions_module = shell_v1
def initialize_app(self, argv):
self.check_options()
self._find_actions(subparsers, actions_module)
self._find_actions(subparsers, self)
def check_options(self):
if self.options.rack_api_version != '1':
raise exceptions.CommandError(
"'rack-api-version' must be 1")
return parser
if not self.options.rack_url:
raise exceptions.CommandError(
"You must provide an RACK url "
"via either --rack-url or env[RACK_URL]")
def _find_actions(self, subparsers, actions_module):
for attr in (a for a in dir(actions_module) if a.startswith('do_')):
command = attr[3:].replace('_', '-')
callback = getattr(actions_module, attr)
desc = callback.__doc__ or ''
action_help = desc.strip()
arguments = getattr(callback, 'arguments', [])
subparser = subparsers.add_parser(
command,
help=action_help,
description=desc,
add_help=False,
)
subparser.add_argument(
'-h', '--help',
action='help',
help=argparse.SUPPRESS,
)
self.subcommands[command] = subparser
for (args, kwargs) in arguments:
subparser.add_argument(*args, **kwargs)
subparser.set_defaults(func=callback)
def setup_debugging(self, debug):
if not debug:
return
streamformat = "%(levelname)s (%(module)s:%(lineno)d) %(message)s"
logging.basicConfig(level=logging.DEBUG,
format=streamformat)
@cliutils.arg('command', metavar='<subcommand>', nargs='?',
help='Display help for <subcommand>')
def do_help(self, args):
"""
Display help about this program or one of its subcommands.
"""
if args.command:
if args.command in self.subcommands:
self.subcommands[args.command].print_help()
else:
def prepare_to_run_command(self, cmd):
commands = ['HelpCommand', 'ListGroups', 'ShowGroup',
'CreateGroup', 'UpdateGroup', 'DeleteGroup',
'InitGroup']
if cmd.__class__.__name__ not in commands:
if not self.options.gid:
raise exceptions.CommandError(
_("'%s' is not a valid subcommand") % args.command)
else:
self.parser.print_help()
"You must provide a gid "
"via either --gid or env[RACK_GID]")
def main(self, argv):
parser = self.get_base_parser()
(options, args) = parser.parse_known_args(argv)
self.setup_debugging(options.debug)
subcommand_parser = self.get_subcommand_parser(
options.rack_api_version)
self.parser = subcommand_parser
if options.help or not argv:
subcommand_parser.print_help()
return 0
args = subcommand_parser.parse_args(argv)
if args.func == self.do_help:
self.do_help(args)
return 0
if not args.rack_url:
raise exceptions.CommandError(
_("You must provide an RACK url "
"via either --rack-url or env[RACK_URL] "))
self.cs = client.Client(options.rack_api_version, rack_url=args.rack_url, http_log_debug=options.debug)
if args.func.func_name[3:].split('_')[0] != 'group' and not args.gid:
raise exceptions.CommandError(
_("You must provide a gid "
"via either --gid or env[RACK_GID] "))
args.func(self.cs, args)
def clean_up(self, cmd, result, err):
if err:
self.log.debug('got an error: %s', err)
def main():
try:
argv = [encodeutils.safe_decode(a) for a in sys.argv[1:]]
RackShell().main(argv)
except Exception as e:
logger.debug(e, exc_info=1)
details = {'name': encodeutils.safe_encode(e.__class__.__name__),
'msg': encodeutils.safe_encode(unicode(e))}
print >> sys.stderr, "ERROR (%(name)s): %(msg)s" % details
sys.exit(1)
except KeyboardInterrupt as e:
print >> sys.stderr, ("Shutting down rackclient")
sys.exit(1)
def main(argv=sys.argv[1:]):
app = RackShell()
return app.run(argv)
if __name__ == "__main__":
main()
if __name__ == '__main__':
sys.exit(main(sys.argv[1:]))

View File

@ -1,128 +0,0 @@
import re
import StringIO
import sys
import fixtures
import mock
from testtools import matchers
from rackclient import exceptions
import rackclient.shell
from rackclient.tests import utils
FAKE_ENV = {'RACK_URL': 'http://www.example.com:8088/v1',
'RACK_GID': '11111111'}
class ShellTest(utils.TestCase):
def make_env(self, exclude=None, fake_env=FAKE_ENV):
env = dict((k, v) for k, v in fake_env.items() if k != exclude)
self.useFixture(fixtures.MonkeyPatch('os.environ', env))
def shell(self, argstr, exitcodes=(0,)):
orig = sys.stdout
orig_stderr = sys.stderr
try:
sys.stdout = StringIO.StringIO()
sys.stderr = StringIO.StringIO()
_shell = rackclient.shell.RackShell()
_shell.main(argstr.split())
except SystemExit:
exc_type, exc_value, exc_traceback = sys.exc_info()
self.assertIn(exc_value.code, exitcodes)
finally:
stdout = sys.stdout.getvalue()
sys.stdout.close()
sys.stdout = orig
stderr = sys.stderr.getvalue()
sys.stderr.close()
sys.stderr = orig_stderr
return (stdout, stderr)
def test_help(self):
required = [
'.*?^usage: ',
'.*?^\s+process-show\s+Show details about the given process',
'.*?^See "rack help COMMAND" for help on a specific command',
]
stdout, stderr = self.shell('help')
for r in required:
self.assertThat((stdout + stderr),
matchers.MatchesRegex(r, re.DOTALL | re.MULTILINE))
def test_help_unknown_command(self):
self.assertRaises(exceptions.CommandError, self.shell, 'help foofoo')
def test_help_on_subcommand(self):
required = [
'.*?^usage: ',
'.*?^Show details about the given process',
'.*?^positional arguments:',
]
stdout, stderr = self.shell('help process-show')
for r in required:
self.assertThat((stdout + stderr),
matchers.MatchesRegex(r, re.DOTALL | re.MULTILINE))
def test_help_no_options(self):
required = [
'.*?^usage: ',
'.*?^\s+process-show\s+Show details about the given process',
'.*?^See "rack help COMMAND" for help on a specific command',
]
stdout, stderr = self.shell('')
for r in required:
self.assertThat((stdout + stderr),
matchers.MatchesRegex(r, re.DOTALL | re.MULTILINE))
def test_no_url(self):
required = ('You must provide an RACK url '
'via either --rack-url or env[RACK_URL] ')
self.make_env(exclude='RACK_URL')
try:
self.shell('process-list')
except exceptions.CommandError as message:
self.assertEqual(required, message.args[0])
else:
self.fail('CommandError not raised')
def test_no_gid(self):
required = ('You must provide a gid '
'via either --gid or env[RACK_GID] ')
self.make_env(exclude='RACK_GID')
try:
self.shell('process-list')
except exceptions.CommandError as message:
self.assertEqual(required, message.args[0])
else:
self.fail('CommandError not raised')
@mock.patch('sys.argv', ['rack'])
@mock.patch('sys.stdout', StringIO.StringIO())
@mock.patch('sys.stderr', StringIO.StringIO())
def test_main_noargs(self):
# Ensure that main works with no command-line arguments
try:
rackclient.shell.main()
except SystemExit:
self.fail('Unexpected SystemExit')
# We expect the normal usage as a result
self.assertIn('Command-line interface to the RACK API',
sys.stdout.getvalue())
@mock.patch('sys.stderr', StringIO.StringIO())
@mock.patch.object(rackclient.shell.RackShell, 'main')
def test_main_exception(self, mock_rack_shell):
mock_rack_shell.side_effect = exceptions.CommandError('error message')
try:
rackclient.shell.main()
except SystemExit as ex:
self.assertEqual(ex.code, 1)
self.assertIn('ERROR (CommandError): error message', sys.stderr.getvalue())
@mock.patch.object(rackclient.shell.RackShell, 'main')
def test_main_keyboard_interrupt(self, mock_rack_shell):
mock_rack_shell.side_effect = KeyboardInterrupt()
try:
rackclient.shell.main()
except SystemExit as ex:
self.assertEqual(ex.code, 1)

View File

@ -14,33 +14,31 @@
from fixtures import fixture
import requests
import testtools
from rackclient import process_context
from rackclient import client
PCTXT = process_context.PCTXT
from mock import *
class PContextFixture(fixture.Fixture):
def setUp(self):
super(PContextFixture, self).setUp()
self.attrs = dir(PCTXT)
self.addCleanup(self.cleanup_pctxt)
PCTXT.gid = 'gid'
PCTXT.pid = 'pid'
PCTXT.ppid = None
PCTXT.proxy_ip = '10.0.0.2'
PCTXT.proxy_url = 'http://10.0.0.2:8088/v1'
PCTXT.fs_endpoint = None
PCTXT.shm_endpoint = None
PCTXT.ipc_endpoint = None
PCTXT.client = client.Client('1', rack_url=PCTXT.proxy_url)
def cleanup_pctxt(self):
attrs = dir(PCTXT)
for attr in attrs:
if attr not in self.attrs: delattr(PCTXT, attr)
# class PContextFixture(fixture.Fixture):
#
# def setUp(self):
# super(PContextFixture, self).setUp()
# self.attrs = dir(PCTXT)
# self.addCleanup(self.cleanup_pctxt)
#
# PCTXT.gid = 'gid'
# PCTXT.pid = 'pid'
# PCTXT.ppid = None
# PCTXT.proxy_ip = '10.0.0.2'
# PCTXT.proxy_url = 'http://10.0.0.2:8088/v1'
# PCTXT.fs_endpoint = None
# PCTXT.shm_endpoint = None
# PCTXT.ipc_endpoint = None
# PCTXT.client = client.Client('1', rack_url=PCTXT.proxy_url)
#
# def cleanup_pctxt(self):
# attrs = dir(PCTXT)
# for attr in attrs:
# if attr not in self.attrs: delattr(PCTXT, attr)
class TestCase(testtools.TestCase):
@ -56,9 +54,32 @@ class TestCase(testtools.TestCase):
# stderr = self.useFixture(fixtures.StringStream('stderr')).stream
# self.useFixture(fixtures.MonkeyPatch('sys.stderr', stderr))
self.useFixture(PContextFixture())
# self.useFixture(PContextFixture())
class LibTestCase(testtools.TestCase):
def setUp(self):
super(LibTestCase, self).setUp()
patcher = patch("rackclient.lib." + self.target_context() + ".RACK_CTX")
self.addCleanup(patcher.stop)
self.mock_RACK_CTX = patcher.start()
self._init_context()
def _init_context(self):
self.mock_RACK_CTX.gid="gid"
self.mock_RACK_CTX.pid="pid"
self.mock_RACK_CTX.ppid=None
self.mock_RACK_CTX.proxy_ip="10.0.0.2"
self.mock_RACK_CTX.proxy_url = 'http://10.0.0.2:8088/v1'
self.mock_RACK_CTX.client = client.Client('1', rack_url=self.mock_RACK_CTX.proxy_url)
self.mock_RACK_CTX.fs_endpoint = None
self.mock_RACK_CTX.ipc_endpoint = None
self.mock_RACK_CTX.shm_endpoint = None
def target_context(self):
pass
class TestResponse(requests.Response):
"""
Class used to wrap requests.Response and provide some

View File

@ -11,20 +11,21 @@
# 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 mock import patch
from swiftclient import exceptions as swift_exc
from mock import patch
from rackclient.tests import utils
from rackclient.v1.syscall.default import file as rackfile
from rackclient import process_context
from rackclient.lib.syscall.default import file as rackfile
from rackclient.exceptions import InvalidFSEndpointError
from rackclient.exceptions import InvalidDirectoryError
from rackclient.exceptions import InvalidFilePathError
from rackclient.exceptions import FileSystemAccessError
PCTXT = process_context.PCTXT
class FileTest(utils.LibTestCase):
class FileTest(utils.TestCase):
def target_context(self):
return "syscall.default.file"
def setUp(self):
super(FileTest, self).setUp()
@ -49,7 +50,7 @@ class FileTest(utils.TestCase):
'"os_password": "password", '
'"os_tenant_name": "tenant", '
'"os_auth_url": "http://www.example.com:5000/v2.0"}')
PCTXT.fs_endpoint = endpoint
self.mock_RACK_CTX.fs_endpoint = endpoint
rackfile._get_swift_client()
expected = {
"user": 'user',
@ -61,7 +62,7 @@ class FileTest(utils.TestCase):
self.mock_conn.assert_any_call(**expected)
def test_get_swift_client_invalid_fs_endpoint_error(self):
PCTXT.fs_endpoint = 'invalid'
self.mock_RACK_CTX.fs_endpoint = 'invalid'
self.assertRaises(InvalidFSEndpointError, rackfile._get_swift_client)
def test_listdir(self):

View File

@ -11,20 +11,21 @@
# 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 mock import patch, Mock
from rackclient import exceptions
from rackclient import process_context
from rackclient.tests import utils
from rackclient.v1.syscall.default import messaging as rack_ipc
import copy
import cPickle
import pika
PCTXT = process_context.PCTXT
from mock import patch, Mock
from rackclient import exceptions
from rackclient.tests import utils
from rackclient.lib.syscall.default import messaging as rack_ipc
class MessagingTest(utils.TestCase):
class MessagingTest(utils.LibTestCase):
def target_context(self):
return "syscall.default.messaging"
def setUp(self):
super(MessagingTest, self).setUp()
@ -44,15 +45,15 @@ class MessagingTest(utils.TestCase):
msg.declare_queue(queue_name)
self.mock_channel.\
exchange_declare.assert_called_with(exchange=PCTXT.gid,
exchange_declare.assert_called_with(exchange=self.mock_RACK_CTX.gid,
type='topic')
self.mock_channel.queue_declare.assert_called_with(queue=queue_name)
r_key = PCTXT.gid + '.' + queue_name
self.mock_channel.queue_bind.assert_called_with(exchange=PCTXT.gid,
r_key = self.mock_RACK_CTX.gid + '.' + queue_name
self.mock_channel.queue_bind.assert_called_with(exchange=self.mock_RACK_CTX.gid,
queue=queue_name,
routing_key=r_key)
@patch('rackclient.v1.syscall.default.messaging.Messaging.Receive')
@patch('rackclient.lib.syscall.default.messaging.Messaging.Receive')
def test_receive_all_msg(self, mock_receive):
timeout_limit = 123
msg = rack_ipc.Messaging()
@ -63,12 +64,12 @@ class MessagingTest(utils.TestCase):
callback_method=mock_receive().time_out)
self.mock_channel.\
basic_consume.assert_called_with(mock_receive().get_all_msg,
queue=PCTXT.pid,
queue=self.mock_RACK_CTX.pid,
no_ack=False)
self.mock_channel.start_consuming.assert_called_with()
self.assertEqual(msg_list, mock_receive().message_list)
@patch('rackclient.v1.syscall.default.messaging.Messaging.Receive')
@patch('rackclient.lib.syscall.default.messaging.Messaging.Receive')
def test_receive_msg(self, mock_receive):
timeout_limit = 123
msg = rack_ipc.Messaging()
@ -79,7 +80,7 @@ class MessagingTest(utils.TestCase):
callback_method=mock_receive().time_out)
self.mock_channel.\
basic_consume.assert_called_with(mock_receive().get_msg,
queue=PCTXT.pid,
queue=self.mock_RACK_CTX.pid,
no_ack=False)
self.mock_channel.start_consuming.assert_called_with()
self.assertEqual(message, mock_receive().message)
@ -90,12 +91,12 @@ class MessagingTest(utils.TestCase):
msg = rack_ipc.Messaging()
msg.send_msg(target,
message=send_msg)
routing_key = PCTXT.gid + '.' + target
send_dict = {'pid': PCTXT.pid,
routing_key = self.mock_RACK_CTX.gid + '.' + target
send_dict = {'pid': self.mock_RACK_CTX.pid,
'message': send_msg}
send_msg = cPickle.dumps(send_dict)
self.mock_channel.\
basic_publish.assert_called_with(exchange=PCTXT.gid,
basic_publish.assert_called_with(exchange=self.mock_RACK_CTX.gid,
routing_key=routing_key,
body=send_msg)
@ -103,12 +104,12 @@ class MessagingTest(utils.TestCase):
msg = rack_ipc.Messaging()
target = 'test_pid'
msg.send_msg(target)
routing_key = PCTXT.gid + '.' + target
send_dict = {'pid': PCTXT.pid}
routing_key = self.mock_RACK_CTX.gid + '.' + target
send_dict = {'pid': self.mock_RACK_CTX.pid}
send_msg = cPickle.dumps(send_dict)
self.mock_channel.\
basic_publish.assert_called_with(exchange=PCTXT.gid,
basic_publish.assert_called_with(exchange=self.mock_RACK_CTX.gid,
routing_key=routing_key,
body=send_msg)
@ -175,39 +176,39 @@ class MessagingTest(utils.TestCase):
receive.time_out()
self.mock_channel.stop_consuming.assert_called_with()
@patch('rackclient.v1.syscall.default.messaging.Messaging',
autospec=True)
def test_init(self, msg):
rack_ipc.init()
msg.assert_called_with()
#@patch('rackclient.v1.syscall.default.messaging.Messaging',
# autospec=True)
# def test_init(self, msg):
# rack_ipc.init()
# msg.assert_called_with()
#
# @patch('rackclient.v1.syscall.default.messaging.Messaging',
# autospec=True)
# def test_init_child(self, msg):
# self.mock_RACK_CTX.ppid = 'PPID'
# receive_msg = {'pid': self.mock_RACK_CTX.ppid}
# mock_messaging = Mock()
# msg.return_value = mock_messaging
# mock_messaging.receive_msg.return_value = receive_msg
# rack_ipc.init()
# mock_messaging.send_msg.asset_called_with(self.mock_RACK_CTX.ppid)
# mock_messaging.receive_msg.assert_called_onece_with()
@patch('rackclient.v1.syscall.default.messaging.Messaging',
autospec=True)
def test_init_child(self, msg):
PCTXT.ppid = 'PPID'
receive_msg = {'pid': PCTXT.ppid}
mock_messaging = Mock()
msg.return_value = mock_messaging
mock_messaging.receive_msg.return_value = receive_msg
rack_ipc.init()
mock_messaging.send_msg.asset_called_with(PCTXT.ppid)
mock_messaging.receive_msg.assert_called_onece_with()
@patch('pika.ConnectionParameters', autospec=True)
def test_create_connection(self, mock_pika_connection_param):
rack_ipc._create_connection()
mock_pika_connection_param.assert_called_with(PCTXT.proxy_ip)
@patch('pika.ConnectionParameters', autospec=True)
def test_create_connection_ipc_endpoint(self, mock_pika_connection_param):
ipc_ip = 'ipc_ip'
PCTXT.ipc_endpoint = ipc_ip
rack_ipc._create_connection()
mock_pika_connection_param.assert_called_with(ipc_ip)
def test_create_connection_amqp_connection_error(self):
self.mock_pika_blocking.side_effect = pika.\
exceptions.AMQPConnectionError()
self.assertRaises(exceptions.AMQPConnectionError,
rack_ipc._create_connection)
# @patch('pika.ConnectionParameters', autospec=True)
# def test_create_connection(self, mock_pika_connection_param):
# rack_ipc._create_connection()
# mock_pika_connection_param.assert_called_with(self.mock_RACK_CTX.proxy_ip)
#
# @patch('pika.ConnectionParameters', autospec=True)
# def test_create_connection_ipc_endpoint(self, mock_pika_connection_param):
# ipc_ip = 'ipc_ip'
# self.mock_RACK_CTX.ipc_endpoint = ipc_ip
#
# rack_ipc._create_connection()
# mock_pika_connection_param.assert_called_with(ipc_ip)
#
# def test_create_connection_amqp_connection_error(self):
# self.mock_pika_blocking.side_effect = pika.\
# exceptions.AMQPConnectionError()
# self.assertRaises(exceptions.AMQPConnectionError,
# rack_ipc._create_connection)

View File

@ -11,16 +11,19 @@
# 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 datetime
from rackclient import exceptions
from mock import patch
from rackclient.tests import utils
from rackclient.v1.syscall.default import pipe
from rackclient import process_context
import datetime
PCTXT = process_context.PCTXT
class PipeTest(utils.TestCase):
from rackclient.lib.syscall.default import pipe
class PipeTest(utils.LibTestCase):
def target_context(self):
return "syscall.default.pipe"
def setUp(self):
super(PipeTest, self).setUp()
patcher = patch('redis.StrictRedis')
@ -100,13 +103,13 @@ class PipeTest(utils.TestCase):
self.ins_redis.lpop.return_value = None
self.ins_redis.hvals.return_value = ["close","close"]
real = pipe.Pipe(read="read", write="write")
self.assertRaises(pipe.EndOfFile, real.read)
self.assertRaises(exceptions.EndOfFile, real.read)
def test_read_NoReadDescriptor(self):
self.ins_redis.lpop.return_value = None
self.ins_redis.hvals.return_value = ["close","close"]
real = pipe.Pipe(read="", write="")
self.assertRaises(pipe.NoReadDescriptor, real.read)
self.assertRaises(exceptions.NoReadDescriptor, real.read)
def test_write(self):
real = pipe.Pipe(read="read",write="write")
@ -117,13 +120,13 @@ class PipeTest(utils.TestCase):
def test_write_NoReadDescriptor(self):
real = pipe.Pipe(read="read",write="write")
self.ins_redis.hvals.return_value = ["close","close"]
self.assertRaises(pipe.NoReadDescriptor, real.write, "data")
self.assertRaises(exceptions.NoReadDescriptor, real.write, "data")
self.assertTrue(self.ins_redis.rpush.call_count == 1)
def test_write_NoWriteDescriptor(self):
real = pipe.Pipe(read="",write="")
self.ins_redis.hvals.return_value = ["close","close"]
self.assertRaises(pipe.NoWriteDescriptor, real.write, "data")
self.assertRaises(exceptions.NoWriteDescriptor, real.write, "data")
self.assertTrue(self.ins_redis.rpush.call_count == 0)
def test_close_reader(self):
@ -214,7 +217,7 @@ class PipeTest(utils.TestCase):
@classmethod
def now(cls):
return mydatetime
patcher = patch("rackclient.v1.syscall.default.pipe.datetime", FakeDateTime)
patcher = patch("rackclient.lib.syscall.default.pipe.datetime", FakeDateTime)
patcher.start()
self.ins_redis.keys.side_effect = [[], ["data"]]
self.ins_redis.hget.side_effect = ["read", "write"]
@ -228,5 +231,5 @@ class PipeTest(utils.TestCase):
self.assertFalse(pipe.Pipe.share("ppid", "pid"))
def test_NoDescriptor_str_(self):
self.assertEquals("Descriptor Not Found", pipe.NoDescriptor().__str__())
self.assertEquals("Descriptor Not Found", exceptions.NoDescriptor().__str__())

View File

@ -13,12 +13,12 @@
# limitations under the License.
from mock import patch
from rackclient.tests import utils
from rackclient.v1.syscall.default import shm
from rackclient import process_context
from rackclient.lib.syscall.default import shm
PCTXT = process_context.PCTXT
class ShmTest(utils.LibTestCase):
class ShmTest(utils.TestCase):
def target_context(self):
return "syscall.default.shm"
def setUp(self):
super(ShmTest, self).setUp()

View File

@ -11,16 +11,17 @@
# 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 mock import patch, Mock
from rackclient import process_context
from rackclient.tests import utils
from rackclient.v1.syscall.default import signal
import copy
PCTXT = process_context.PCTXT
from mock import patch, Mock
from rackclient.tests import utils
from rackclient.lib.syscall.default import signal
class SignalTest(utils.TestCase):
class SignalTest(utils.LibTestCase):
def target_context(self):
return "syscall.default.signal"
def setUp(self):
super(SignalTest, self).setUp()
@ -37,7 +38,7 @@ class SignalTest(utils.TestCase):
mock_websocket_websocketapp.\
assert_called_with(url=s.url + '/receive',
header=['PID: ' + PCTXT.pid],
header=['PID: ' + self.mock_RACK_CTX.pid],
on_message=s.on_message,
on_error=s.on_error,
on_close=s.on_close)
@ -73,7 +74,7 @@ class SignalTest(utils.TestCase):
s = signal.SignalManager()
on_msg_func = 'on_msg_func'
PCTXT.pid = None
self.mock_RACK_CTX.pid = None
self.assertRaises(Exception, s.receive, on_msg_func)
def test_on_message(self):

View File

@ -11,39 +11,35 @@
# 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 rackclient import process_context
from rackclient.tests import utils
from rackclient.v1 import processes
from rackclient.v1.syscall.default import file
from rackclient.v1.syscall.default import messaging
from rackclient.v1.syscall.default import pipe
from rackclient.v1.syscall.default import syscall
from rackclient.lib.syscall.default import messaging, file, syscall, pipe
from mock import call
from mock import Mock
PCTXT = process_context.PCTXT
class SyscallTest(utils.LibTestCase):
class SyscallTest(utils.TestCase):
def target_context(self):
return "syscall.default.syscall"
def setUp(self):
super(SyscallTest, self).setUp()
def test_fork(self):
def create_process(gid, ppid, **kwargs):
count = PCTXT.client.processes.create.call_count
count = self.mock_RACK_CTX.client.processes.create.call_count
d = {'ppid': ppid,
'pid': 'pid' + str(count),
'gid': gid,
'proxy_ip': PCTXT.proxy_ip}
'proxy_ip': self.mock_RACK_CTX.proxy_ip}
args = kwargs['args']
args.update(d)
kwargs.update(d)
return_process = processes.Process(PCTXT.client, kwargs)
return_process = processes.Process(self.mock_RACK_CTX.client, kwargs)
return return_process
PCTXT.client.processes.create = Mock(side_effect=create_process)
self.mock_RACK_CTX.client.processes.create = Mock(side_effect=create_process)
# messaging mock
mock_messaging = Mock()
messaging.Messaging = Mock(return_value=mock_messaging)
@ -74,28 +70,28 @@ class SyscallTest(utils.TestCase):
expected_arg_list = [arg1, arg2, arg3]
for process in process_list:
self.assertTrue(process.args in expected_arg_list)
self.assertEqual(process.ppid, PCTXT.pid)
self.assertEqual(process.gid, PCTXT.gid)
self.assertEqual(process.ppid, self.mock_RACK_CTX.pid)
self.assertEqual(process.gid, self.mock_RACK_CTX.gid)
expected_arg_list.remove(process.args)
def test_bulk_fork_check_connection_recoverable_error(self):
# setup
def create_process(gid, ppid, **kwargs):
count = PCTXT.client.processes.create.call_count
count = self.mock_RACK_CTX.client.processes.create.call_count
if count == 2:
raise Exception()
d = {'ppid': ppid,
'pid': 'pid' + str(count),
'gid': gid,
'proxy_ip': PCTXT.proxy_ip}
'proxy_ip': self.mock_RACK_CTX.proxy_ip}
args = kwargs['args']
args.update(d)
kwargs.update(d)
return_process = processes.Process(PCTXT.client, kwargs)
return_process = processes.Process(self.mock_RACK_CTX.client, kwargs)
return return_process
PCTXT.client.processes.create = Mock(side_effect=create_process)
PCTXT.client.processes.delete = Mock()
self.mock_RACK_CTX.client.processes.create = Mock(side_effect=create_process)
self.mock_RACK_CTX.client.processes.delete = Mock()
# messaging mock
mock_messaging = Mock()
@ -116,7 +112,7 @@ class SyscallTest(utils.TestCase):
process_list = syscall.fork(arg_list)
# check
PCTXT.client.processes.delete.assert_called_with(PCTXT.gid, 'pid1')
self.mock_RACK_CTX.client.processes.delete.assert_called_with(self.mock_RACK_CTX.gid, 'pid1')
expected_pipe_share = [call('pid', 'pid3'),
call('pid', 'pid4'),
call('pid', 'pid5')]
@ -129,12 +125,12 @@ class SyscallTest(utils.TestCase):
expected_arg_list = [arg1, arg2, arg3]
for process in process_list:
self.assertTrue(process.args in expected_arg_list)
self.assertEqual(process.ppid, PCTXT.pid)
self.assertEqual(process.gid, PCTXT.gid)
self.assertEqual(process.ppid, self.mock_RACK_CTX.pid)
self.assertEqual(process.gid, self.mock_RACK_CTX.gid)
expected_arg_list.remove(process.args)
def test_bulk_fork_error_no_child_process_is_created(self):
PCTXT.client.processes.create = Mock(side_effect=Exception)
self.mock_RACK_CTX.client.processes.create = Mock(side_effect=Exception)
# call fork
arg1 = {'test': 'test1'}
arg2 = {'test': 'test2'}
@ -147,19 +143,19 @@ class SyscallTest(utils.TestCase):
def test_check_connection_error_no_child_process_is_active(self):
# setup
def create_process(gid, ppid, **kwargs):
count = PCTXT.client.processes.create.call_count
count = self.mock_RACK_CTX.client.processes.create.call_count
d = {'ppid': ppid,
'pid': 'pid' + str(count),
'gid': gid,
'proxy_ip': PCTXT.proxy_ip}
'proxy_ip': self.mock_RACK_CTX.proxy_ip}
args = kwargs['args']
args.update(d)
kwargs.update(d)
return_process = processes.Process(PCTXT.client, kwargs)
return_process = processes.Process(self.mock_RACK_CTX.client, kwargs)
return return_process
PCTXT.client.processes.create = Mock(side_effect=create_process)
PCTXT.client.processes.delete = Mock()
self.mock_RACK_CTX.client.processes.create = Mock(side_effect=create_process)
self.mock_RACK_CTX.client.processes.delete = Mock()
# messaging mock
mock_messaging = Mock()
messaging.Messaging = Mock(return_value=mock_messaging)
@ -171,11 +167,11 @@ class SyscallTest(utils.TestCase):
{'args': {'test': 'test2'}},
{'args': {'test': 'test3'}}]
self.assertRaises(Exception, syscall.fork, arg_list)
expected_processes_delete = [call(PCTXT.gid, 'pid1'),
call(PCTXT.gid, 'pid2'),
call(PCTXT.gid, 'pid3')]
expected_processes_delete = [call(self.mock_RACK_CTX.gid, 'pid1'),
call(self.mock_RACK_CTX.gid, 'pid2'),
call(self.mock_RACK_CTX.gid, 'pid3')]
self.assertEqual(expected_processes_delete,
PCTXT.client.processes.delete.call_args_list)
self.mock_RACK_CTX.client.processes.delete.call_args_list)
def test_pipe_no_arg(self):
pipe.Pipe = Mock()

View File

@ -1,486 +0,0 @@
# Copyright (c) 2014 ITOCHU Techno-Solutions Corporation.
#
# 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 ConfigParser import NoOptionError
import StringIO
import copy
import fixtures
import mock
from mock import mock_open
import os
import re
from testtools import matchers
from rackclient import exceptions
import rackclient.shell
from rackclient.v1.proxy import ProxyManager
from rackclient.tests.v1 import fakes
from rackclient.tests import utils
class BaseShellTest(utils.TestCase):
FAKE_ENV = {
'RACK_URL': 'http://www.example.com:8088/v1',
'RACK_GID': '11111111',
}
def setUp(self):
"""Run before each test."""
super(BaseShellTest, self).setUp()
for var in self.FAKE_ENV:
self.useFixture(fixtures.EnvironmentVariable(var,
self.FAKE_ENV[var]))
self.shell = rackclient.shell.RackShell()
self.useFixture(fixtures.MonkeyPatch(
'rackclient.client.get_client_class',
lambda *_: fakes.FakeClient))
def assert_called(self, method, url, body=None, **kwargs):
self.shell.cs.assert_called(method, url, body, **kwargs)
def assert_called_anytime(self, method, url, body=None):
self.shell.cs.assert_called_anytime(method, url, body)
@mock.patch('sys.stdout', new_callable=StringIO.StringIO)
def run_command(self, cmd, mock_stdout):
if isinstance(cmd, list):
self.shell.main(cmd)
else:
self.shell.main(cmd.split())
return mock_stdout.getvalue()
class ShellTest(BaseShellTest):
def test_group_list(self):
self.run_command('group-list')
self.assert_called('GET', '/groups')
def test_group_show(self):
self.run_command('group-show 11111111')
self.assert_called('GET', '/groups/11111111', pos=-6)
self.assert_called('GET', '/groups/11111111/keypairs', pos=-5)
self.assert_called('GET', '/groups/11111111/securitygroups', pos=-4)
self.assert_called('GET', '/groups/11111111/networks', pos=-3)
self.assert_called('GET', '/groups/11111111/processes', pos=-2)
self.assert_called('GET', '/groups/11111111/proxy', pos=-1)
@mock.patch.object(ProxyManager, 'get', side_effect=Exception())
def test_group_show_no_proxy(self, mock_proxy_manager):
stdout = self.run_command('group-show 11111111')
required = '.*?^\|\s+proxy \(pid\)\s+\|\s+\|'
self.assertThat(stdout,
matchers.MatchesRegex(required, re.DOTALL | re.MULTILINE))
def test_group_create(self):
self.run_command('group-create --description detail group1')
self.assert_called('POST', '/groups')
def test_group_update(self):
self.run_command('group-update --name group2 --description detail2 11111111')
self.assert_called('PUT', '/groups/11111111')
def test_group_delete(self):
self.run_command('group-delete 11111111')
self.assert_called('DELETE', '/groups/11111111')
def test_keypair_list(self):
self.run_command('keypair-list')
self.assert_called('GET', '/groups/11111111/keypairs')
def test_keypair_show(self):
self.run_command('keypair-show aaaaaaaa')
self.assert_called('GET', '/groups/11111111/keypairs/aaaaaaaa')
def test_keypair_create(self):
self.run_command('keypair-create --name keypair1 '
'--is_default true')
self.assert_called('POST', '/groups/11111111/keypairs')
def test_keypair_update(self):
self.run_command('keypair-update --is_default false aaaaaaaa')
self.assert_called('PUT', '/groups/11111111/keypairs/aaaaaaaa')
def test_keypair_delete(self):
self.run_command('keypair-delete aaaaaaaa')
self.assert_called('DELETE', '/groups/11111111/keypairs/aaaaaaaa')
def test_securitygroup_list(self):
self.run_command('securitygroup-list')
self.assert_called('GET', '/groups/11111111/securitygroups')
def test_securitygroup_show(self):
self.run_command('securitygroup-show aaaaaaaa')
self.assert_called('GET', '/groups/11111111/securitygroups/aaaaaaaa')
def test_securitygroup_create(self):
self.run_command('securitygroup-create --name securitygroup1 '
'--is_default true '
'--rule protocol=tcp,port_range_max=80,'
'port_range_min=80,remote_ip_prefix=10.0.0.0/24 '
'--rule protocol=icmp,remote_ip_prefix=10.0.0.0/24')
self.assert_called('POST', '/groups/11111111/securitygroups')
def test_securitygroup_update(self):
self.run_command('securitygroup-update --is_default false aaaaaaaa')
self.assert_called('PUT', '/groups/11111111/securitygroups/aaaaaaaa')
def test_securitygroup_delete(self):
self.run_command('securitygroup-delete aaaaaaaa')
self.assert_called('DELETE', '/groups/11111111/securitygroups/aaaaaaaa')
def test_network_list(self):
self.run_command('network-list')
self.assert_called('GET', '/groups/11111111/networks')
def test_network_show(self):
self.run_command('network-show aaaaaaaa')
self.assert_called('GET', '/groups/11111111/networks/aaaaaaaa')
def test_network_create(self):
self.run_command('network-create --name network1 '
'--is_admin true '
'--gateway_ip 10.0.0.254 '
'--dns_nameserver 8.8.8.8 '
'--dns_nameserver 8.8.4.4 '
'--ext_router_id rrrrrrrr '
'10.0.0.0/24')
self.assert_called('POST', '/groups/11111111/networks')
def test_network_delete(self):
self.run_command('network-delete aaaaaaaa')
self.assert_called('DELETE', '/groups/11111111/networks/aaaaaaaa')
def test_process_list(self):
self.run_command('process-list')
self.assert_called('GET', '/groups/11111111/processes')
def test_process_show(self):
self.run_command('process-show aaaaaaaa')
self.assert_called('GET', '/groups/11111111/processes/aaaaaaaa')
def test_process_create(self):
test_userdata = os.path.join(os.path.dirname(__file__), 'test_userdata.txt')
self.run_command('process-create --ppid aaaaaaaa '
'--name process1 '
'--nova_flavor_id yyyyyyyy '
'--glance_image_id xxxxxxxx '
'--keypair_id iiiiiiii '
'--securitygroup_id jjjjjjjj '
'--securitygroup_id kkkkkkkk '
'--userdata %s '
'--args key1=value1,key2=value2' % test_userdata)
self.assert_called('POST', '/groups/11111111/processes')
def test_process_create_with_no_option(self):
self.run_command('process-create')
self.assert_called('POST', '/groups/11111111/processes')
def test_process_could_not_open_userdata_file(self):
try:
self.run_command('process-create --ppid aaaaaaaa '
'--userdata not_exists.txt')
except exceptions.CommandError as e:
required = ".*?^Can't open 'not_exists.txt'"
self.assertThat(e.message,
matchers.MatchesRegex(required, re.DOTALL | re.MULTILINE))
def test_process_with_args_file(self):
test_args = os.path.join(os.path.dirname(__file__), 'test_args.txt')
self.run_command('process-create --ppid aaaaaaaa '
'--args %s' % test_args)
self.assert_called('POST', '/groups/11111111/processes')
@mock.patch('__builtin__.open', side_effect=IOError())
def test_process_could_not_open_args_file(self, m_open):
try:
test_args = os.path.join(os.path.dirname(__file__), 'test_args.txt')
self.run_command('process-create --ppid aaaaaaaa '
'--args %s' % test_args)
except exceptions.CommandError as e:
required = ".*?^Can't open '.*?rackclient/tests/v1/test_args.txt'"
self.assertThat(str(e.message),
matchers.MatchesRegex(required, re.DOTALL | re.MULTILINE))
def test_process_invalid_args_file(self):
invalid_args = os.path.join(os.path.dirname(__file__), 'test_invalid_args.txt')
try:
self.run_command('process-create --ppid aaaaaaaa '
'--args %s' % invalid_args)
except exceptions.CommandError as e:
required = ('.*?rackclient/tests/v1/test_invalid_args.txt '
'is not the format of key=value lines')
self.assertThat(e.message,
matchers.MatchesRegex(required, re.DOTALL | re.MULTILINE))
def test_process_invalid_args(self):
try:
self.run_command('process-create --ppid aaaaaaaa '
'--args key1value1')
except exceptions.CommandError as e:
required = '.*?^\'key1value1\' is not in the format of key=value'
self.assertThat(str(e.message),
matchers.MatchesRegex(required, re.DOTALL | re.MULTILINE))
def test_process_update(self):
self.run_command('process-update --app_status ACTIVE aaaaaaaa')
self.assert_called('PUT', '/groups/11111111/processes/aaaaaaaa')
def test_process_delete(self):
self.run_command('process-delete aaaaaaaa')
self.assert_called('DELETE', '/groups/11111111/processes/aaaaaaaa')
def test_proxy_show(self):
self.run_command('proxy-show')
self.assert_called('GET', '/groups/11111111/proxy')
def test_proxy_create(self):
test_userdata = os.path.join(os.path.dirname(__file__), 'test_userdata.txt')
self.run_command('proxy-create '
'--name proxy '
'--nova_flavor_id yyyyyyyy '
'--glance_image_id xxxxxxxx '
'--keypair_id iiiiiiii '
'--securitygroup_id jjjjjjjj '
'--securitygroup_id kkkkkkkk '
'--userdata %s '
'--args key1=value1,key2=value2' % test_userdata)
self.assert_called('POST', '/groups/11111111/proxy')
def test_proxy_create_with_no_option(self):
self.run_command('proxy-create')
self.assert_called('POST', '/groups/11111111/proxy')
def test_procexy_could_not_open_userdata_file(self):
try:
self.run_command('proxy-create '
'--userdata not_exists.txt')
except exceptions.CommandError as e:
required = ".*?^Can't open 'not_exists.txt'"
self.assertThat(e.message,
matchers.MatchesRegex(required, re.DOTALL | re.MULTILINE))
def test_proxy_with_args_file(self):
test_args = os.path.join(os.path.dirname(__file__), 'test_args.txt')
self.run_command('proxy-create '
'--args %s' % test_args)
self.assert_called('POST', '/groups/11111111/proxy')
@mock.patch('__builtin__.open', side_effect=IOError())
def test_proxy_could_not_open_args_file(self, m_open):
try:
test_args = os.path.join(os.path.dirname(__file__), 'test_args.txt')
self.run_command('proxy-create '
'--args %s' % test_args)
except exceptions.CommandError as e:
required = ".*?^Can't open '.*?rackclient/tests/v1/test_args.txt'"
self.assertThat(str(e.message),
matchers.MatchesRegex(required, re.DOTALL | re.MULTILINE))
def test_proxy_invalid_args_file(self):
invalid_args = os.path.join(os.path.dirname(__file__), 'test_invalid_args.txt')
try:
self.run_command('proxy-create '
'--args %s' % invalid_args)
except exceptions.CommandError as e:
required = ('.*?rackclient/tests/v1/test_invalid_args.txt '
'is not the format of key=value lines')
self.assertThat(e.message,
matchers.MatchesRegex(required, re.DOTALL | re.MULTILINE))
def test_proxy_invalid_args(self):
try:
self.run_command('proxy-create '
'--args key1value1')
except exceptions.CommandError as e:
required = '.*?^\'key1value1\' is not in the format of key=value'
self.assertThat(str(e.message),
matchers.MatchesRegex(required, re.DOTALL | re.MULTILINE))
def test_proxy_update(self):
self.run_command('proxy-update --app_status ACTIVE '
'--shm_endpoint shm_endpoint '
'--ipc_endpoint ipc_endpoint '
'--fs_endpoint fs_endpoint')
self.assert_called('PUT', '/groups/11111111/proxy')
class ShellGroupInitTest(BaseShellTest):
CONFIG = {
'group': {
'name': 'group1',
'description': 'This is group1'
},
'keypair': {
'name': 'keypair1',
'is_default': 't'
},
'securitygroup': {
'name': 'securitygroup1',
'is_default': 't',
'rules': 'protocol=tcp,port_range_max=80,'
'port_range_min=80,remote_ip_prefix=10.0.0.0/24 '
'protocol=icmp,remote_ip_prefix=10.0.0.0/24'
},
'network': {
'cidr': '10.0.0.0/24',
'name': 'network1',
'is_admin': 't',
'gateway_ip': '10.0.0.254',
'dns_nameservers': '8.8.8.8 8.8.4.4',
'ext_router_id': 'rrrrrrrr'
},
'proxy': {
'name': 'proxy',
'nova_flavor_id': 'yyyyyyyy',
'glance_image_id': 'xxxxxxxx',
'args': 'key1=value1,key2=value2'
}
}
def setUp(self):
super(ShellGroupInitTest, self).setUp()
self.config = copy.deepcopy(self.CONFIG)
self.patcher = mock.patch('rackclient.v1.shell.ConfigParser')
self.mock_config = self.patcher.start()
def tearDown(self):
super(BaseShellTest, self).tearDown()
self.patcher.stop()
def _fake_get(self, section, key):
try:
return self.config[section][key]
except KeyError:
raise NoOptionError(key, section)
def test_group_init(self):
test_userdata = os.path.join(os.path.dirname(__file__),
'test_userdata.txt')
self.config['proxy']['userdata'] = test_userdata
self.mock_config.return_value.get = self._fake_get
self.run_command('group-init /path/to/group.conf')
def test_group_init_with_required(self):
self.config['group'].pop('description')
self.config['keypair'].pop('name')
self.config['keypair'].pop('is_default')
self.config['securitygroup'].pop('name')
self.config['securitygroup'].pop('is_default')
self.config['securitygroup'].pop('rules')
self.config['network'].pop('name')
self.config['network'].pop('is_admin')
self.config['network'].pop('gateway_ip')
self.config['network'].pop('dns_nameservers')
self.config['network'].pop('ext_router_id')
self.config['proxy'].pop('name')
self.config['proxy'].pop('args')
self.mock_config.return_value.get = self._fake_get
self.run_command('group-init /path/to/group.conf')
def test_group_init_without_group_name(self):
self.config['group'].pop('name')
self.mock_config.return_value.get = self._fake_get
try:
self.run_command('group-init /path/to/group.conf')
except exceptions.CommandError as e:
required = '.*?^Group name is required.'
self.assertThat(
str(e.message),
matchers.MatchesRegex(required,
re.DOTALL | re.MULTILINE))
def test_group_init_invalid_securitygroup_rules(self):
self.config['securitygroup']['rules'] = 'invalid'
self.mock_config.return_value.get = self._fake_get
try:
self.run_command('group-init /path/to/group.conf')
except exceptions.CommandError as e:
required = ('.*?^Could not create a securitygroup: '
'securitygroup rules are not valid formart: '
'\'.*\' is not in the format of key=value')
self.assertThat(
str(e.message),
matchers.MatchesRegex(required,
re.DOTALL | re.MULTILINE))
def test_group_init_without_network_cidr(self):
self.config['network'].pop('cidr')
self.mock_config.return_value.get = self._fake_get
try:
self.run_command('group-init /path/to/group.conf')
except exceptions.CommandError as e:
required = '.*?^Network cidr is required.'
self.assertThat(
str(e.message),
matchers.MatchesRegex(required,
re.DOTALL | re.MULTILINE))
def test_group_init_without_proxy_nova_flavor_id(self):
self.config['proxy'].pop('nova_flavor_id')
self.mock_config.return_value.get = self._fake_get
try:
self.run_command('group-init /path/to/group.conf')
except exceptions.CommandError as e:
required = '.*?^Flavor id is required.'
self.assertThat(
str(e.message),
matchers.MatchesRegex(required,
re.DOTALL | re.MULTILINE))
def test_group_init_without_proxy_glance_image_id(self):
self.config['proxy'].pop('glance_image_id')
self.mock_config.return_value.get = self._fake_get
try:
self.run_command('group-init /path/to/group.conf')
except exceptions.CommandError as e:
required = '.*?^Image id is required.'
self.assertThat(
str(e.message),
matchers.MatchesRegex(required,
re.DOTALL | re.MULTILINE))
@mock.patch('__builtin__.open', side_effect=IOError())
def test_group_init_could_not_open_userdata(self, m_open):
self.config['proxy']['userdata'] = 'fake_userdata.txt'
self.mock_config.return_value.get = self._fake_get
try:
self.run_command('group-init /path/to/group.conf')
except exceptions.CommandError as e:
required = '.*?^Can\'t open fake_userdata.txt.'
self.assertThat(
str(e.message),
matchers.MatchesRegex(required,
re.DOTALL | re.MULTILINE))
def test_group_init_invalid_args(self):
self.config['proxy']['args'] = 'key1:value1'
self.mock_config.return_value.get = self._fake_get
try:
self.run_command('group-init /path/to/group.conf')
except exceptions.CommandError as e:
required = ('.*?^\'key1:value1\' is not '
'in the format of key=value')
self.assertThat(
str(e.message),
matchers.MatchesRegex(required,
re.DOTALL | re.MULTILINE))

View File

@ -11,26 +11,24 @@
# 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 rackclient import process_context
from rackclient.v1.syscall.default import messaging
from rackclient import exceptions
PCTXT = process_context.PCTXT
import argparse
def _is_exist():
def keyvalue_to_dict(string):
"""
Return a dict made from comma separated key-value strings
:param string: comma separated key-value pairs
like 'key1=value1,key2=value2'
:return: dict
"""
try:
PCTXT.client.processes.get(PCTXT.gid, PCTXT.pid)
except exceptions.NotFound:
msg = "This process is not recognized by RACK"
raise exceptions.InvalidProcessError(msg)
def init():
try:
process_context.init()
_is_exist()
messaging.init()
except Exception as e:
msg = "Failed to initialize the process: %s." % e.message
raise exceptions.ProcessInitError(msg)
d = {}
pairs = string.split(',')
for pair in pairs:
(k, v) = pair.split('=', 1)
d.update({k: v})
return d
except ValueError:
msg = "%r is not in the format of key=value" % string
raise argparse.ArgumentTypeError(msg)

View File

@ -0,0 +1,13 @@
# Copyright (c) 2014 ITOCHU Techno-Solutions Corporation.
#
# 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,338 @@
# Copyright (c) 2014 ITOCHU Techno-Solutions Corporation.
#
# 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
from ConfigParser import ConfigParser
from ConfigParser import NoOptionError
from cliff.command import Command
from cliff.lister import Lister
from cliff.show import ShowOne
from rackclient import client
from rackclient import exceptions
from rackclient import utils
def _make_print_data(gid, name, description, user_id, project_id,
status, keypairs=None, securitygroups=None,
networks=None, proxy=None, processes=None):
columns = ['gid', 'name', 'description', 'user_id', 'project_id', 'status']
data = [gid, name, description, user_id, project_id, status]
if keypairs is not None:
columns.append('keypairs')
data.append(keypairs)
if securitygroups is not None:
columns.append('securitygroups')
data.append(securitygroups)
if networks is not None:
columns.append('networks')
data.append(networks)
if proxy is not None:
columns.append('proxy (pid)')
data.append(proxy)
if processes is not None:
columns.append('processes (pid)')
data.append(processes)
return columns, data
class ListGroups(Lister):
"""
Print a list of all groups.
"""
def __init__(self, app, app_args):
super(ListGroups, self).__init__(app, app_args)
# When the help command is called,
# the type of 'app_args' is list.
if isinstance(app_args, argparse.Namespace):
self.client = client.Client(app_args.rack_api_version,
rack_url=app_args.rack_url,
http_log_debug=app_args.debug)
def take_action(self, parsed_args):
groups = self.client.groups.list()
return (
('gid', 'name', 'description', 'status'),
((g.gid, g.name, g.description, g.status) for g in groups)
)
class ShowGroup(ShowOne):
"""
Show details about the given group.
"""
def __init__(self, app, app_args):
super(ShowGroup, self).__init__(app, app_args)
# When the help command is called,
# the type of 'app_args' is list.
if isinstance(app_args, argparse.Namespace):
self.client = client.Client(app_args.rack_api_version,
rack_url=app_args.rack_url,
http_log_debug=app_args.debug)
def get_parser(self, prog_name):
parser = super(ShowGroup, self).get_parser(prog_name)
parser.add_argument('gid', metavar='<gid>',
default=os.environ.get('RACK_GID'),
help="Group id")
return parser
def take_action(self, parsed_args):
group = self.client.groups.get(parsed_args.gid)
keypairs = self.client.keypairs.list(parsed_args.gid)
securitygroups = self.client.securitygroups.list(parsed_args.gid)
networks = self.client.networks.list(parsed_args.gid)
processes = self.client.processes.list(parsed_args.gid)
try:
proxy = self.client.proxy.get(parsed_args.gid)
except Exception:
proxy = None
return _make_print_data(
group.gid,
group.name,
group.description,
group.user_id,
group.project_id,
group.status,
','.join([k.keypair_id for k in keypairs]),
','.join([s.securitygroup_id for s in securitygroups]),
','.join([n.network_id for n in networks]),
','.join([p.pid for p in processes]),
proxy.pid if proxy else ''
)
class CreateGroup(ShowOne):
"""
Create a new group.
"""
def __init__(self, app, app_args):
super(CreateGroup, self).__init__(app, app_args)
# When the help command is called,
# the type of 'app_args' is list.
if isinstance(app_args, argparse.Namespace):
self.client = client.Client(app_args.rack_api_version,
rack_url=app_args.rack_url,
http_log_debug=app_args.debug)
def get_parser(self, prog_name):
parser = super(CreateGroup, self).get_parser(prog_name)
parser.add_argument('name', metavar='<name>',
help="Name of the new group")
parser.add_argument('--description', metavar='<description>',
help="Details of the new group")
return parser
def take_action(self, parsed_args):
group = self.client.groups.create(parsed_args.name, parsed_args.description)
return _make_print_data(
group.gid,
group.name,
group.description,
group.user_id,
group.project_id,
group.status
)
class UpdateGroup(ShowOne):
"""
Update the specified group.
"""
def __init__(self, app, app_args):
super(UpdateGroup, self).__init__(app, app_args)
# When the help command is called,
# the type of 'app_args' is list.
if isinstance(app_args, argparse.Namespace):
self.client = client.Client(app_args.rack_api_version,
rack_url=app_args.rack_url,
http_log_debug=app_args.debug)
def get_parser(self, prog_name):
parser = super(UpdateGroup, self).get_parser(prog_name)
parser.add_argument('gid', metavar='<gid>',
help="Group id")
parser.add_argument('--name', metavar='<name>',
help="Name of the group")
parser.add_argument('--description', metavar='<description>',
help="Details of the group")
return parser
def take_action(self, parsed_args):
group = self.client.groups.update(parsed_args.gid,
parsed_args.name,
parsed_args.description)
return _make_print_data(
group.gid,
group.name,
group.description,
group.user_id,
group.project_id,
group.status
)
class DeleteGroup(Command):
"""
Delete the specified group.
"""
def __init__(self, app, app_args):
super(DeleteGroup, self).__init__(app, app_args)
# When the help command is called,
# the type of 'app_args' is list.
if isinstance(app_args, argparse.Namespace):
self.client = client.Client(app_args.rack_api_version,
rack_url=app_args.rack_url,
http_log_debug=app_args.debug)
def get_parser(self, prog_name):
parser = super(DeleteGroup, self).get_parser(prog_name)
parser.add_argument('gid', metavar='<gid>',
help="Group id")
return parser
def take_action(self, parsed_args):
group = self.client.groups.delete(parsed_args.gid)
class InitGroup(ShowOne):
"""
Create a group, a keypair, a security group, a network and
a rack-proxy based on the specified configuration file.
"""
def __init__(self, app, app_args):
super(InitGroup, self).__init__(app, app_args)
# When the help command is called,
# the type of 'app_args' is list.
if isinstance(app_args, argparse.Namespace):
self.client = client.Client(app_args.rack_api_version,
rack_url=app_args.rack_url,
http_log_debug=app_args.debug)
def get_parser(self, prog_name):
parser = super(InitGroup, self).get_parser(prog_name)
parser.add_argument('config', metavar='<config-file>',
help="Configuration file including parameters of the new group")
return parser
def take_action(self, parsed_args):
config = ConfigParser()
config.read(parsed_args.config)
group_name = None
group_description = None
keypair_name = None
keypair_is_default = True
securitygroup_name = None
securitygroup_is_default = True
securitygroup_rules= []
network_cidr = None
network_name = None
network_is_admin = True
network_gateway_ip = None
network_dns_nameservers = []
network_ext_router_id = None
proxy_name = None
proxy_flavor = None
proxy_image = None
try:
group_name = config.get('group', 'name')
except NoOptionError:
raise exceptions.CommandError("Group name is required.")
try:
network_cidr = config.get('network', 'cidr')
except NoOptionError:
raise exceptions.CommandError("Network cidr is required.")
try:
securitygroup_rules = config.get('securitygroup', 'rules').split()
securitygroup_rules = \
[utils.keyvalue_to_dict(r) for r in securitygroup_rules]
except argparse.ArgumentTypeError:
raise exceptions.CommandError((
"Could not create a securitygroup: "
"securitygroup rules are not valid formart"))
try:
network_ext_router_id = config.get('network', 'ext_router_id')
except NoOptionError:
raise exceptions.CommandError("Router ID is required.")
try:
proxy_flavor = config.get('proxy', 'nova_flavor_id')
except NoOptionError:
raise exceptions.CommandError("Flavor ID is required.")
try:
proxy_image = config.get('proxy', 'glance_image_id')
except NoOptionError:
raise exceptions.CommandError("Image ID is required.")
try:
group_description = config.get('group', 'description')
keypair_name = config.get('keypair', 'name')
keypair_is_default = config.get('keypair', 'is_default')
securitygroup_name = config.get('securitygroup', 'name')
securitygroup_is_default = config.get('securitygroup', 'is_default')
network_name = config.get('network', 'name')
network_is_admin = config.get('network', 'is_admin')
network_gateway_ip = config.get('network', 'gateway_ip')
network_dns_nameservers = config.get('network', 'dns_nameservers').split()
except NoOptionError:
pass
group = self.client.groups.create(group_name, group_description)
keypair = self.client.keypairs.create(group.gid, keypair_name,
keypair_is_default)
securitygroup = self.client.securitygroups.create(
group.gid,
securitygroup_name,
securitygroup_is_default,
securitygroup_rules)
network = self.client.networks.create(
group.gid, network_cidr, network_name,
network_is_admin, network_gateway_ip,
network_dns_nameservers,
network_ext_router_id)
proxy = self.client.proxy.create(
group.gid, name=proxy_name,
nova_flavor_id=proxy_flavor,
glance_image_id=proxy_image,
keypair_id=keypair.keypair_id,
securitygroup_ids=[securitygroup.securitygroup_id])
columns = ['gid', 'keypair_id', 'securitygroup_id', 'network_id',
'proxy_pid', 'proxy_name']
data = [group.gid, keypair.keypair_id, securitygroup.securitygroup_id,
network.network_id, proxy.pid, proxy.name]
return columns, data

View File

@ -0,0 +1,199 @@
# Copyright (c) 2014 ITOCHU Techno-Solutions Corporation.
#
# 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
from cliff.command import Command
from cliff.lister import Lister
from cliff.show import ShowOne
from rackclient import client
def _make_print_data(keypair_id, name, nova_keypair_id, is_default, private_key,
gid, user_id, project_id, status=None):
columns = ['keypair_id', 'name', 'nova_keypair_id', 'is_default',
'private_key', 'gid', 'user_id', 'project_id']
data = [keypair_id, name, nova_keypair_id, is_default,
private_key, gid, user_id, project_id]
if status is not None:
columns.append('status')
data.append(status)
return columns, data
class ListKeypairs(Lister):
"""
Print a list of all keypairs in the specified group.
"""
def __init__(self, app, app_args):
super(ListKeypairs, self).__init__(app, app_args)
# When the help command is called,
# the type of 'app_args' is list.
if isinstance(app_args, argparse.Namespace):
self.client = client.Client(app_args.rack_api_version,
rack_url=app_args.rack_url,
http_log_debug=app_args.debug)
self.gid = app_args.gid
def take_action(self, parsed_args):
keypairs = self.client.keypairs.list(self.gid)
return (
('keypair_id', 'name', 'is_default', 'status'),
((k.keypair_id, k.name, k.is_default, k.status) for k in keypairs)
)
class ShowKeypair(ShowOne):
"""
Show details about the given keypair.
"""
def __init__(self, app, app_args):
super(ShowKeypair, self).__init__(app, app_args)
# When the help command is called,
# the type of 'app_args' is list.
if isinstance(app_args, argparse.Namespace):
self.client = client.Client(app_args.rack_api_version,
rack_url=app_args.rack_url,
http_log_debug=app_args.debug)
self.gid = app_args.gid
def get_parser(self, prog_name):
parser = super(ShowKeypair, self).get_parser(prog_name)
parser.add_argument('keypair_id', metavar='<keypair-id>',
help="Keypair ID")
return parser
def take_action(self, parsed_args):
keypair = self.client.keypairs.get(self.gid,
parsed_args.keypair_id)
return _make_print_data(
keypair.keypair_id,
keypair.name,
keypair.nova_keypair_id,
keypair.is_default,
keypair.private_key,
keypair.gid,
keypair.user_id,
keypair.project_id,
keypair.status
)
class CreateKeypair(ShowOne):
"""
Create a new keypair.
"""
def __init__(self, app, app_args):
super(CreateKeypair, self).__init__(app, app_args)
# When the help command is called,
# the type of 'app_args' is list.
if isinstance(app_args, argparse.Namespace):
self.client = client.Client(app_args.rack_api_version,
rack_url=app_args.rack_url,
http_log_debug=app_args.debug)
self.gid = app_args.gid
def get_parser(self, prog_name):
parser = super(CreateKeypair, self).get_parser(prog_name)
parser.add_argument('--name', metavar='<name>',
help="Name of the new keypair")
parser.add_argument('--is-default', metavar='<true/false>',
default=False,
help="Defaults to the default keypair of the group")
return parser
def take_action(self, parsed_args):
keypair = self.client.keypairs.create(self.gid, parsed_args.name,
parsed_args.is_default)
return _make_print_data(
keypair.keypair_id,
keypair.name,
keypair.nova_keypair_id,
keypair.is_default,
keypair.private_key,
keypair.gid,
keypair.user_id,
keypair.project_id,
)
class UpdateKeypair(ShowOne):
"""
Update the specified keypair.
"""
def __init__(self, app, app_args):
super(UpdateKeypair, self).__init__(app, app_args)
# When the help command is called,
# the type of 'app_args' is list.
if isinstance(app_args, argparse.Namespace):
self.client = client.Client(app_args.rack_api_version,
rack_url=app_args.rack_url,
http_log_debug=app_args.debug)
self.gid = app_args.gid
def get_parser(self, prog_name):
parser = super(UpdateKeypair, self).get_parser(prog_name)
parser.add_argument('keypair_id', metavar='<keypair-id>',
help="Keypair ID")
parser.add_argument('--is-default', metavar='<true/false>',
required=True,
help="Defaults to the default keypair of the group")
return parser
def take_action(self, parsed_args):
keypair = self.client.keypairs.update(self.gid,
parsed_args.keypair_id,
parsed_args.is_default)
return _make_print_data(
keypair.keypair_id,
keypair.name,
keypair.nova_keypair_id,
keypair.is_default,
keypair.private_key,
keypair.gid,
keypair.user_id,
keypair.project_id,
)
class DeleteKeypair(Command):
"""
Delete the specified keypair.
"""
def __init__(self, app, app_args):
super(DeleteKeypair, self).__init__(app, app_args)
# When the help command is called,
# the type of 'app_args' is list.
if isinstance(app_args, argparse.Namespace):
self.client = client.Client(app_args.rack_api_version,
rack_url=app_args.rack_url,
http_log_debug=app_args.debug)
self.gid = app_args.gid
def get_parser(self, prog_name):
parser = super(DeleteKeypair, self).get_parser(prog_name)
parser.add_argument('keypair_id', metavar='<keypair-id>',
help="Keypair ID")
return parser
def take_action(self, parsed_args):
keypair = self.client.keypairs.delete(self.gid, parsed_args.keypair_id)

View File

@ -0,0 +1,100 @@
# Copyright (c) 2014 ITOCHU Techno-Solutions Corporation.
#
# 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
from cliff.show import ShowOne
from rackclient import client
class Montecarlo(ShowOne):
"""
The application to approximate the circular constant
with the Monte Carlo method
"""
def __init__(self, app, app_args):
super(Montecarlo, self).__init__(app, app_args)
# When the help command is called,
# the type of 'app_args' is list.
if isinstance(app_args, argparse.Namespace):
self.client = client.Client(app_args.rack_api_version,
rack_url=app_args.rack_url,
http_log_debug=app_args.debug)
self.gid = app_args.gid
def get_parser(self, prog_name):
parser = super(Montecarlo, self).get_parser(prog_name)
parser.add_argument('--msg_limit_time', metavar='<integer>',
default=300, type=int,
help="Parent waits for notifications of "
"preparation completion from children "
"until the timer reaches to msg_limit_time "
"seconds")
parser.add_argument('--noterm', metavar='<true/false>',
default=False,
help="(Intended for debugging) "
"If true, all processes won't be deleted")
parser.add_argument('--image', metavar='<image-id>',
required=True,
help="(Required) ID of the montecarlo image")
parser.add_argument('--flavor', metavar='<flavor-id>',
required=True,
help="(Required) ID of a flavor")
parser.add_argument('--trials', metavar='<integer>',
required=True, type=int,
help="(Required) The number of trials in a "
"simulation")
parser.add_argument('--workers', metavar='<integer>',
required=True, type=int,
help="(Required) The number of workers that "
"will be launched")
parser.add_argument('--stdout', metavar='</file/path>',
required=True,
help="(Required) File path on Swift to output "
"the simulation report to")
return parser
def take_action(self, parsed_args):
options = {
"trials": parsed_args.trials,
"workers": parsed_args.workers,
"stdout": parsed_args.stdout,
"msg_limit_time": parsed_args.msg_limit_time,
"noterm": parsed_args.noterm
}
process = self.client.processes.create(
self.gid, name="montecarlo",
nova_flavor_id=parsed_args.flavor,
glance_image_id=parsed_args.image,
args=options)
process_args = process.args
process_args.pop('gid')
process_args.pop('pid')
process_args.pop('ppid', None)
process_args.pop('proxy_ip', None)
process_args.pop('rackapi_ip', None)
cmd = process.name
for k, v in sorted(process_args.items()):
cmd += ' --' + k + ' ' + v
columns = ['pid', 'ppid', 'cmd']
data = [process.pid, process.ppid, cmd]
return columns, data

View File

@ -0,0 +1,179 @@
# Copyright (c) 2014 ITOCHU Techno-Solutions Corporation.
#
# 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
from cliff.command import Command
from cliff.lister import Lister
from cliff.show import ShowOne
from rackclient import client
from rackclient import exceptions
def _make_print_data(network_id, name, neutron_network_id, is_admin,
cidr, ext_router_id, gid, user_id, project_id,
status=None):
columns = ['network_id', 'name', 'neutron_network_id', 'is_admin',
'cidr', 'ext_router_id', 'gid', 'user_id', 'project_id']
data = [network_id, name, neutron_network_id, is_admin,
cidr, ext_router_id, gid, user_id, project_id]
if status is not None:
columns.append('status')
data.append(status)
return columns, data
class ListNetworks(Lister):
"""
Print a list of all networks in the specified group.
"""
def __init__(self, app, app_args):
super(ListNetworks, self).__init__(app, app_args)
# When the help command is called,
# the type of 'app_args' is list.
if isinstance(app_args, argparse.Namespace):
self.client = client.Client(app_args.rack_api_version,
rack_url=app_args.rack_url,
http_log_debug=app_args.debug)
self.gid = app_args.gid
def take_action(self, parsed_args):
networks = self.client.networks.list(self.gid)
return (
('network_id', 'name', 'is_admin', 'status'),
((n.network_id, n.name, n.is_admin, n.status) for n in networks)
)
class ShowNetwork(ShowOne):
"""
Show details about the given network group.
"""
def __init__(self, app, app_args):
super(ShowNetwork, self).__init__(app, app_args)
# When the help command is called,
# the type of 'app_args' is list.
if isinstance(app_args, argparse.Namespace):
self.client = client.Client(app_args.rack_api_version,
rack_url=app_args.rack_url,
http_log_debug=app_args.debug)
self.gid = app_args.gid
def get_parser(self, prog_name):
parser = super(ShowNetwork, self).get_parser(prog_name)
parser.add_argument('network_id', metavar='<network-id>',
help="Network ID")
return parser
def take_action(self, parsed_args):
network = self.client.networks.get(self.gid,
parsed_args.network_id)
return _make_print_data(
network.network_id,
network.name,
network.neutron_network_id,
network.is_admin,
network.cidr,
network.ext_router_id,
network.gid,
network.user_id,
network.project_id,
network.status,
)
class CreateNetwork(ShowOne):
"""
Create a new securitygroup.
"""
def __init__(self, app, app_args):
super(CreateNetwork, self).__init__(app, app_args)
# When the help command is called,
# the type of 'app_args' is list.
if isinstance(app_args, argparse.Namespace):
self.client = client.Client(app_args.rack_api_version,
rack_url=app_args.rack_url,
http_log_debug=app_args.debug)
self.gid = app_args.gid
def get_parser(self, prog_name):
parser = super(CreateNetwork, self).get_parser(prog_name)
parser.add_argument('cidr', metavar='<cidr>',
help="Cidr of the new network")
parser.add_argument('--name', metavar='<name>',
help="Name of the new securitygroup")
parser.add_argument('--is-admin', metavar='<true/false>',
default=False,
help="")
parser.add_argument('--gateway-ip', metavar='<x.x.x.x>',
help="Gateway ip address of the new network")
parser.add_argument('--dns-nameserver', metavar='<x.x.x.x>',
dest='dns_nameservers', action='append',
help="DNS server for the new network (Can be repeated)")
parser.add_argument('--ext-router-id', metavar='<router-id>',
help="Router id the new network connects to")
return parser
def take_action(self, parsed_args):
network = self.client.networks.create(self.gid,
parsed_args.cidr,
parsed_args.name,
parsed_args.is_admin,
parsed_args.gateway_ip,
parsed_args.dns_nameservers,
parsed_args.ext_router_id)
return _make_print_data(
network.network_id,
network.name,
network.neutron_network_id,
network.is_admin,
network.cidr,
network.ext_router_id,
network.gid,
network.user_id,
network.project_id
)
class DeleteNetwork(Command):
"""
Delete the specified network.
"""
def __init__(self, app, app_args):
super(DeleteNetwork, self).__init__(app, app_args)
# When the help command is called,
# the type of 'app_args' is list.
if isinstance(app_args, argparse.Namespace):
self.client = client.Client(app_args.rack_api_version,
rack_url=app_args.rack_url,
http_log_debug=app_args.debug)
self.gid = app_args.gid
def get_parser(self, prog_name):
parser = super(DeleteNetwork, self).get_parser(prog_name)
parser.add_argument('network_id', metavar='<network-id>',
help="Network ID")
return parser
def take_action(self, parsed_args):
network = self.client.networks.delete(self.gid,
parsed_args.network_id)

View File

@ -0,0 +1,249 @@
# Copyright (c) 2014 ITOCHU Techno-Solutions Corporation.
#
# 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
from cliff.command import Command
from cliff.lister import Lister
from cliff.show import ShowOne
from rackclient import client
from rackclient import exceptions
from rackclient import utils
class PS(Lister):
"""
Print a list of all processes in the specified group.
"""
def __init__(self, app, app_args):
super(PS, self).__init__(app, app_args)
# When the help command is called,
# the type of 'app_args' is list.
if isinstance(app_args, argparse.Namespace):
self.client = client.Client(app_args.rack_api_version,
rack_url=app_args.rack_url,
http_log_debug=app_args.debug)
self.gid = app_args.gid
def take_action(self, parsed_args):
processes = self.client.processes.list(self.gid)
def _make_command(process):
p_args = process.args
p_args.pop('gid')
p_args.pop('pid')
p_args.pop('ppid', None)
p_args.pop('proxy_ip', None)
p_args.pop('rackapi_ip', None)
cmd = process.name
for k, v in sorted(p_args.items()):
cmd += ' --' + k + ' ' + v
return cmd
return (
('pid', 'ppid', 'command'),
((p.pid, p.ppid, _make_command(p)) for p in processes)
)
def _make_print_data(pid, ppid, name, nova_instance_id, nova_flavor_id,
glance_image_id, keypair_id, securitygroup_ids, networks,
userdata, args, app_status, gid, user_id, project_id, status=None):
columns = ['pid', 'ppid', 'name', 'nova_instance_id', 'nova_flavor_id',
'glance_image_id', 'keypair_id', 'securitygroup_ids', 'networks',
'userdata', 'args', 'app_status', 'gid', 'user_id', 'project_id']
data = [pid, ppid, name, nova_instance_id, nova_flavor_id,
glance_image_id, keypair_id, securitygroup_ids, networks,
userdata, args, app_status, gid, user_id, project_id]
if status is not None:
columns.append('status')
data.append(status)
return columns, data
class Show(ShowOne):
"""
Show details about the given process.
"""
def __init__(self, app, app_args):
super(Show, self).__init__(app, app_args)
# When the help command is called,
# the type of 'app_args' is list.
if isinstance(app_args, argparse.Namespace):
self.client = client.Client(app_args.rack_api_version,
rack_url=app_args.rack_url,
http_log_debug=app_args.debug)
self.gid = app_args.gid
def get_parser(self, prog_name):
parser = super(Show, self).get_parser(prog_name)
parser.add_argument('pid', metavar='<pid>',
help="process ID")
return parser
def take_action(self, parsed_args):
process = self.client.processes.get(self.gid, parsed_args.pid)
sg_ids = process.securitygroup_ids
if sg_ids:
sg_ids = ','.join(sg_ids)
process_args = process.args
if process_args:
s = ''
for k, v in sorted(process_args.items()):
s += k + '=' + v + '\n'
process_args = s.rstrip('\n')
return _make_print_data(
process.pid,
process.ppid,
process.name,
process.nova_instance_id,
process.nova_flavor_id,
process.glance_image_id,
process.keypair_id,
sg_ids,
process.networks,
process.userdata,
process_args,
process.app_status,
process.gid,
process.user_id,
process.project_id,
process.status
)
class Boot(ShowOne):
"""
Boot a process.
"""
def __init__(self, app, app_args):
super(Boot, self).__init__(app, app_args)
# When the help command is called,
# the type of 'app_args' is list.
if isinstance(app_args, argparse.Namespace):
self.client = client.Client(app_args.rack_api_version,
rack_url=app_args.rack_url,
http_log_debug=app_args.debug)
self.gid = app_args.gid
def get_parser(self, prog_name):
parser = super(Boot, self).get_parser(prog_name)
parser.add_argument('--ppid', metavar='<ppid>',
help="ID of a parent process")
parser.add_argument('--name', metavar='<name>',
help="Name of the new process")
parser.add_argument('--flavor', metavar='<flavor-id>',
help="ID of a flavor that is provided by Nova")
parser.add_argument('--image', metavar='<image-id>',
help="ID of a image that is provided by Glance")
parser.add_argument('--keypair', metavar='<keypair-id>',
help="Keypair ID")
parser.add_argument('--securitygroup', metavar='<securitygroup-id>',
dest='securitygroup_ids', action='append',
default=[],
help="Securitygroup ID (Can be repeated)")
parser.add_argument('--userdata', metavar='</file/path>',
help="Userdata file path")
parser.add_argument('--args', metavar='<key1=value1,key2=value2,...>',
type=utils.keyvalue_to_dict,
help="Key-value pairs to be passed to metadata server")
return parser
def take_action(self, parsed_args):
userdata = None
if parsed_args.userdata:
try:
userdata = open(parsed_args.userdata)
except IOError:
raise exceptions.CommandError(
"Can't open '%s'" % parsed_args.userdata)
process = self.client.processes.create(
gid=self.gid,
ppid=parsed_args.ppid,
name=parsed_args.name,
nova_flavor_id=parsed_args.flavor,
glance_image_id=parsed_args.image,
keypair_id=parsed_args.keypair,
securitygroup_ids=parsed_args.securitygroup_ids,
userdata=userdata,
args=parsed_args.args)
sg_ids = process.securitygroup_ids
if sg_ids:
sg_ids = ','.join(sg_ids)
process_args = process.args
if process_args:
s = ''
for k, v in sorted(process_args.items()):
s += k + '=' + v + '\n'
process_args = s.rstrip('\n')
return _make_print_data(
process.pid,
process.ppid,
process.name,
process.nova_instance_id,
process.nova_flavor_id,
process.glance_image_id,
process.keypair_id,
sg_ids,
process.networks,
process.userdata,
process_args,
process.app_status,
process.gid,
process.user_id,
process.project_id,
)
class Kill(Command):
"""
Delete the specified process.
"""
def __init__(self, app, app_args):
super(Kill, self).__init__(app, app_args)
# When the help command is called,
# the type of 'app_args' is list.
if isinstance(app_args, argparse.Namespace):
self.client = client.Client(app_args.rack_api_version,
rack_url=app_args.rack_url,
http_log_debug=app_args.debug)
self.gid = app_args.gid
def get_parser(self, prog_name):
parser = super(Kill, self).get_parser(prog_name)
parser.add_argument('pid', metavar='<pid>',
help="process ID")
return parser
def take_action(self, parsed_args):
process = self.client.processes.delete(self.gid, parsed_args.pid)

View File

@ -0,0 +1,239 @@
# Copyright (c) 2014 ITOCHU Techno-Solutions Corporation.
#
# 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
from cliff.command import Command
from cliff.lister import Lister
from cliff.show import ShowOne
from rackclient import client
def _make_print_data(pid, ppid, name, nova_instance_id, nova_flavor_id,
glance_image_id, keypair_id, securitygroup_ids, networks,
userdata, args, app_status, fs_endpoint, ipc_endpoint,
shm_endpoint, gid, user_id, project_id, status=None):
columns = ['pid', 'ppid', 'name', 'nova_instance_id', 'nova_flavor_id',
'glance_image_id', 'keypair_id', 'securitygroup_ids', 'networks',
'userdata', 'args', 'app_status', 'fs_endpoint', 'ipc_endpoint',
'shm_endpoint', 'gid', 'user_id', 'project_id']
data = [pid, ppid, name, nova_instance_id, nova_flavor_id,
glance_image_id, keypair_id, securitygroup_ids, networks,
userdata, args, app_status, fs_endpoint, ipc_endpoint,
shm_endpoint, gid, user_id, project_id]
if status is not None:
columns.append('status')
data.append(status)
return columns, data
class ShowProxy(ShowOne):
"""
Show details about the rack-proxy process.
"""
def __init__(self, app, app_args):
super(ShowProxy, self).__init__(app, app_args)
# When the help command is called,
# the type of 'app_args' is list.
if isinstance(app_args, argparse.Namespace):
self.client = client.Client(app_args.rack_api_version,
rack_url=app_args.rack_url,
http_log_debug=app_args.debug)
self.gid = app_args.gid
def take_action(self, parsed_args):
proxy = self.client.proxy.get(self.gid)
sg_ids = proxy.securitygroup_ids
if sg_ids:
sg_ids = ','.join(sg_ids)
proxy_args = proxy.args
if proxy_args:
s = ''
for k, v in sorted(proxy_args.items()):
s += k + '=' + v + '\n'
proxy_args = s.rstrip('\n')
return _make_print_data(
proxy.pid,
proxy.ppid,
proxy.name,
proxy.nova_instance_id,
proxy.nova_flavor_id,
proxy.glance_image_id,
proxy.keypair_id,
sg_ids,
proxy.networks,
proxy.userdata,
proxy_args,
proxy.app_status,
proxy.fs_endpoint,
proxy.ipc_endpoint,
proxy.shm_endpoint,
proxy.gid,
proxy.user_id,
proxy.project_id,
proxy.status
)
class CreateProxy(ShowOne):
"""
Create a rack-proxy process.
"""
def __init__(self, app, app_args):
super(CreateProxy, self).__init__(app, app_args)
# When the help command is called,
# the type of 'app_args' is list.
if isinstance(app_args, argparse.Namespace):
self.client = client.Client(app_args.rack_api_version,
rack_url=app_args.rack_url,
http_log_debug=app_args.debug)
self.gid = app_args.gid
def get_parser(self, prog_name):
parser = super(CreateProxy, self).get_parser(prog_name)
parser.add_argument('--name', metavar='<name>',
help="Name of the rack-proxy process")
parser.add_argument('--keypair', metavar='<keypair-id>',
help="Keypair id of the new process uses")
parser.add_argument('--securitygroup', metavar='<securitygroup-id>',
dest='securitygroup', action='append',
default=[],
help="Securitygroup id the rack-proxy process belongs to (Can be repeated)")
parser.add_argument('--flavor', metavar='<nova-flavor-id>',
required=True,
help="(Required) Flavor id of the rack-proxy process")
parser.add_argument('--image', metavar='<glance-image-id>',
required=True,
help="(Required) Image id that registered on Glance of the rack-proxy process")
return parser
def take_action(self, parsed_args):
proxy = self.client.proxy.create(self.gid,
name=parsed_args.name,
nova_flavor_id=parsed_args.flavor,
glance_image_id=parsed_args.image,
keypair_id=parsed_args.keypair,
securitygroup_ids=parsed_args.securitygroup)
sg_ids = proxy.securitygroup_ids
if sg_ids:
sg_ids = ','.join(sg_ids)
proxy_args = proxy.args
if proxy_args:
s = ''
for k, v in sorted(proxy_args.items()):
s += k + '=' + v + '\n'
proxy_args = s.rstrip('\n')
return _make_print_data(
proxy.pid,
proxy.ppid,
proxy.name,
proxy.nova_instance_id,
proxy.nova_flavor_id,
proxy.glance_image_id,
proxy.keypair_id,
sg_ids,
proxy.networks,
proxy.userdata,
proxy_args,
proxy.app_status,
proxy.fs_endpoint,
proxy.ipc_endpoint,
proxy.shm_endpoint,
proxy.gid,
proxy.user_id,
proxy.project_id,
proxy.status
)
class UpdateProxy(ShowOne):
"""
Update the rack-proxy process.
"""
def __init__(self, app, app_args):
super(UpdateProxy, self).__init__(app, app_args)
# When the help command is called,
# the type of 'app_args' is list.
if isinstance(app_args, argparse.Namespace):
self.client = client.Client(app_args.rack_api_version,
rack_url=app_args.rack_url,
http_log_debug=app_args.debug)
self.gid = app_args.gid
def get_parser(self, prog_name):
parser = super(UpdateProxy, self).get_parser(prog_name)
parser.add_argument('--fs-endpoint', metavar='<fs-endpoint>',
help="Endpoint of the shared memory service")
parser.add_argument('--ipc-endpoint', metavar='<ipc-endpoint>',
help="Endpoint of the IPC service")
parser.add_argument('--shm-endpoint', metavar='<shm-endpoint>',
help="Endpoint of the file system service")
parser.add_argument('--app-status', metavar='<app-status>',
help="Application layer status of the proxy")
return parser
def take_action(self, parsed_args):
proxy = self.client.proxy.update(self.gid,
parsed_args.fs_endpoint,
parsed_args.ipc_endpoint,
parsed_args.shm_endpoint,
parsed_args.app_status)
sg_ids = proxy.securitygroup_ids
if sg_ids:
sg_ids = ','.join(sg_ids)
proxy_args = proxy.args
if proxy_args:
s = ''
for k, v in sorted(proxy_args.items()):
s += k + '=' + v + '\n'
proxy_args = s.rstrip('\n')
return _make_print_data(
proxy.pid,
proxy.ppid,
proxy.name,
proxy.nova_instance_id,
proxy.nova_flavor_id,
proxy.glance_image_id,
proxy.keypair_id,
sg_ids,
proxy.networks,
proxy.userdata,
proxy_args,
proxy.app_status,
proxy.fs_endpoint,
proxy.ipc_endpoint,
proxy.shm_endpoint,
proxy.gid,
proxy.user_id,
proxy.project_id
)

View File

@ -0,0 +1,218 @@
# Copyright (c) 2014 ITOCHU Techno-Solutions Corporation.
#
# 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
from cliff.command import Command
from cliff.lister import Lister
from cliff.show import ShowOne
from rackclient import client
from rackclient import exceptions
from rackclient import utils
def _make_print_data(securitygroup_id, name, neutron_securitygroup_id,
is_default, gid, user_id, project_id, status=None):
columns = ['securitygroup_id', 'name', 'neutron_securitygroup_id',
'is_default', 'gid', 'user_id', 'project_id']
data = [securitygroup_id, name, neutron_securitygroup_id,
is_default, gid, user_id, project_id]
if status is not None:
columns.append('status')
data.append(status)
return columns, data
class ListSecuritygroups(Lister):
"""
Print a list of all security groups in the specified group.
"""
def __init__(self, app, app_args):
super(ListSecuritygroups, self).__init__(app, app_args)
# When the help command is called,
# the type of 'app_args' is list.
if isinstance(app_args, argparse.Namespace):
self.client = client.Client(app_args.rack_api_version,
rack_url=app_args.rack_url,
http_log_debug=app_args.debug)
self.gid = app_args.gid
def take_action(self, parsed_args):
securitygroups = self.client.securitygroups.list(self.gid)
return (
('securitygroup_id', 'name', 'is_default', 'status'),
((s.securitygroup_id, s.name, s.is_default, s.status) for s in securitygroups)
)
class ShowSecuritygroup(ShowOne):
"""
Show details about the given security group.
"""
def __init__(self, app, app_args):
super(ShowSecuritygroup, self).__init__(app, app_args)
# When the help command is called,
# the type of 'app_args' is list.
if isinstance(app_args, argparse.Namespace):
self.client = client.Client(app_args.rack_api_version,
rack_url=app_args.rack_url,
http_log_debug=app_args.debug)
self.gid = app_args.gid
def get_parser(self, prog_name):
parser = super(ShowSecuritygroup, self).get_parser(prog_name)
parser.add_argument('securitygroup_id', metavar='<securitygroup-id>',
help="Securitygroup ID")
return parser
def take_action(self, parsed_args):
securitygroup = self.client.securitygroups.get(self.gid,
parsed_args.securitygroup_id)
return _make_print_data(
securitygroup.securitygroup_id,
securitygroup.name,
securitygroup.neutron_securitygroup_id,
securitygroup.is_default,
securitygroup.gid,
securitygroup.user_id,
securitygroup.project_id,
securitygroup.status,
)
class CreateSecuritygroup(ShowOne):
"""
Create a new securitygroup.
"""
def __init__(self, app, app_args):
super(CreateSecuritygroup, self).__init__(app, app_args)
# When the help command is called,
# the type of 'app_args' is list.
if isinstance(app_args, argparse.Namespace):
self.client = client.Client(app_args.rack_api_version,
rack_url=app_args.rack_url,
http_log_debug=app_args.debug)
self.gid = app_args.gid
def get_parser(self, prog_name):
parser = super(CreateSecuritygroup, self).get_parser(prog_name)
parser.add_argument('--name', metavar='<name>',
help="Name of the new securitygroup")
parser.add_argument('--is-default', metavar='<true/false>',
default=False,
help="Defaults to the default securitygroup of the group")
parser.add_argument('--rule',
metavar=("<protocol=tcp|udp|icmp,port_range_max=integer,"
"port_range_min=integer,remote_ip_prefix=cidr,"
"remote_securitygroup_id=securitygroup_uuid>"),
action='append',
type=utils.keyvalue_to_dict,
dest='rules',
default=[],
help=("Securitygroup rules. "
"protocol: Protocol of packet, "
"port_range_max: Starting port range, "
"port_range_min: Ending port range, "
"remote_ip_prefix: CIDR to match on, "
"remote_securitygroup_id: Remote securitygroup id "
"to apply rule. (Can be repeated)"))
return parser
def take_action(self, parsed_args):
securitygroup = self.client.securitygroups.create(self.gid,
parsed_args.name,
parsed_args.is_default,
parsed_args.rules)
return _make_print_data(
securitygroup.securitygroup_id,
securitygroup.name,
securitygroup.neutron_securitygroup_id,
securitygroup.is_default,
securitygroup.gid,
securitygroup.user_id,
securitygroup.project_id,
)
class UpdateSecuritygroup(ShowOne):
"""
Update the specified securitygroup.
"""
def __init__(self, app, app_args):
super(UpdateSecuritygroup, self).__init__(app, app_args)
# When the help command is called,
# the type of 'app_args' is list.
if isinstance(app_args, argparse.Namespace):
self.client = client.Client(app_args.rack_api_version,
rack_url=app_args.rack_url,
http_log_debug=app_args.debug)
self.gid = app_args.gid
def get_parser(self, prog_name):
parser = super(UpdateSecuritygroup, self).get_parser(prog_name)
parser.add_argument('securitygroup_id', metavar='<securitygroup-id>',
help="Securitygroup ID")
parser.add_argument('--is-default', metavar='<true/false>',
required=True,
help="Defaults to the default securitygroup of the group")
return parser
def take_action(self, parsed_args):
securitygroup = self.client.securitygroups.update(self.gid,
parsed_args.securitygroup_id,
parsed_args.is_default)
return _make_print_data(
securitygroup.securitygroup_id,
securitygroup.name,
securitygroup.neutron_securitygroup_id,
securitygroup.is_default,
securitygroup.gid,
securitygroup.user_id,
securitygroup.project_id,
)
class DeleteSecuritygroup(Command):
"""
Delete the specified securitygroup.
"""
def __init__(self, app, app_args):
super(DeleteSecuritygroup, self).__init__(app, app_args)
# When the help command is called,
# the type of 'app_args' is list.
if isinstance(app_args, argparse.Namespace):
self.client = client.Client(app_args.rack_api_version,
rack_url=app_args.rack_url,
http_log_debug=app_args.debug)
self.gid = app_args.gid
def get_parser(self, prog_name):
parser = super(DeleteSecuritygroup, self).get_parser(prog_name)
parser.add_argument('securitygroup_id', metavar='<securitygroup-id>',
help="Securitygroup ID")
return parser
def take_action(self, parsed_args):
securitygroup = self.client.securitygroups.delete(self.gid,
parsed_args.securitygroup_id)

View File

@ -1,805 +0,0 @@
# Copyright (c) 2014 ITOCHU Techno-Solutions Corporation.
#
# 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 ConfigParser import ConfigParser
from ConfigParser import NoOptionError
import argparse
import os
import prettytable
from oslo.utils import strutils
from rackclient import exceptions
from rackclient.openstack.common import cliutils
from rackclient.openstack.common.gettextutils import _
from rackclient.v1.syscall.default import signal
from rackclient.v1.syscall.default import file as rackfile
def _keyvalue_to_dict(text):
try:
d = {}
pairs = text.split(',')
for pair in pairs:
(k, v) = pair.split('=', 1)
d.update({k: v})
return d
except ValueError:
msg = "%r is not in the format of key=value" % text
raise argparse.ArgumentTypeError(msg)
def do_group_list(cs, args):
"""
Print a list of all groups.
"""
groups = cs.groups.list()
fields = ['gid', 'name', 'description', 'status']
print_list(groups, fields, sortby='gid')
@cliutils.arg(
'gid',
metavar='<gid>',
help=_("Group id"))
def do_group_show(cs, args):
"""
Show details about the given group.
"""
group = cs.groups.get(args.gid)
keypairs = cs.keypairs.list(args.gid)
securitygroups = cs.securitygroups.list(args.gid)
networks = cs.networks.list(args.gid)
processes = cs.processes.list(args.gid)
try:
proxy = cs.proxy.get(args.gid)
except Exception:
proxy = None
d = group._info
d.update({
"keypairs": ','.join([x.keypair_id for x in keypairs]),
"securitygroups": ','.join([x.securitygroup_id for x in securitygroups]),
"networks": ','.join([x.network_id for x in networks]),
"processes (pid)": ','.join([x.pid for x in processes]),
"proxy (pid)": proxy.pid if proxy else ''
})
print_dict(d)
@cliutils.arg(
'name',
metavar='<name>',
help=_("Name of the new group"))
@cliutils.arg(
'--description',
metavar='<description>',
help=_("Details of the new group"))
def do_group_create(cs, args):
"""
Create a new group.
"""
group = cs.groups.create(args.name, args.description)
d = group._info
print_dict(d)
@cliutils.arg(
'gid',
metavar='<gid>',
help=_("Group id"))
@cliutils.arg(
'--name',
metavar='<name>',
help=_("Name of the group"))
@cliutils.arg(
'--description',
metavar='<description>',
help=_("Details of the group"))
def do_group_update(cs, args):
"""
Update the specified group.
"""
group = cs.groups.update(args.gid, args.name, args.description)
d = group._info
print_dict(d)
@cliutils.arg(
'gid',
metavar='<gid>',
help=_("Group id"))
def do_group_delete(cs, args):
"""
Delete the specified group.
"""
cs.groups.delete(args.gid)
def do_keypair_list(cs, args):
"""
Print a list of all keypairs in the specified group.
"""
keypairs = cs.keypairs.list(args.gid)
fields = ['keypair_id', 'name', 'is_default', 'status']
print_list(keypairs, fields, sortby='keypair_id')
@cliutils.arg(
'keypair_id',
metavar='<keypair_id>',
help=_("Keypair ID"))
def do_keypair_show(cs, args):
"""
Show details about the given keypair.
"""
keypair = cs.keypairs.get(args.gid, args.keypair_id)
d = keypair._info
print_dict(d)
@cliutils.arg(
'--name',
metavar='<name>',
help=_("Name of the new keypair"))
@cliutils.arg(
'--is_default',
metavar='<is_default>',
help=_("Defaults to the default keypair of the group"),
type=lambda v: strutils.bool_from_string(v, True),
default=False)
def do_keypair_create(cs, args):
"""
Create a new keypair.
"""
keypair = cs.keypairs.create(args.gid, args.name, args.is_default)
d = keypair._info
print_dict(d)
@cliutils.arg(
'keypair_id',
metavar='<keypair_id>',
help=_("Keypair id"))
@cliutils.arg(
'--is_default',
metavar='<is_default>',
help=_("Defaults to the default keypair of the group"),
type=lambda v: strutils.bool_from_string(v, True),
default=True)
def do_keypair_update(cs, args):
"""
Update the specified keypair.
"""
keypair = cs.keypairs.update(args.gid, args.keypair_id, args.is_default)
d = keypair._info
print_dict(d)
@cliutils.arg(
'keypair_id',
metavar='<keypair_id>',
help=_("Keypair id"))
def do_keypair_delete(cs, args):
"""
Delete the specified keypair.
"""
cs.keypairs.delete(args.gid, args.keypair_id)
def do_securitygroup_list(cs, args):
"""
Print a list of all security groups in the specified group.
"""
securitygroups = cs.securitygroups.list(args.gid)
fields = [
'securitygroup_id', 'name', 'is_default', 'status'
]
print_list(securitygroups, fields, sortby='securitygroup_id')
@cliutils.arg(
'securitygroup_id',
metavar='<securitygroup_id>',
help=_("Securitygroup id"))
def do_securitygroup_show(cs, args):
"""
Show details about the given security group.
"""
securitygroup = cs.securitygroups.get(args.gid, args.securitygroup_id)
d = securitygroup._info
print_dict(d)
@cliutils.arg(
'--name',
metavar='<name>',
help=_("Name of the new securitygroup"))
@cliutils.arg(
'--is_default',
metavar='<is_default>',
help=_("Defaults to the default securitygroup of the group"),
type=lambda v: strutils.bool_from_string(v, True),
default=False)
@cliutils.arg(
'--rule',
metavar="<protocol=tcp|udp|icmp,port_range_max=integer,"
"port_range_min=integer,remote_ip_prefix=cidr,"
"remote_securitygroup_id=securitygroup_uuid>",
action='append',
type=_keyvalue_to_dict,
dest='rules',
default=[],
help=_("Securitygroup rules. "
"protocol: Protocol of packet, "
"port_range_max: Starting port range, "
"port_range_min: Ending port range, "
"remote_ip_prefix: CIDR to match on, "
"remote_securitygroup_id: Remote securitygroup id to apply rule. "
"(Can be repeated)"))
def do_securitygroup_create(cs, args):
"""
Create a new security group.
"""
securitygroup = cs.securitygroups.create(args.gid,
args.name,
args.is_default,
args.rules)
d = securitygroup._info
print_dict(d)
@cliutils.arg(
'securitygroup_id',
metavar='<securitygroup_id>',
help=_("Securitygroup id"))
@cliutils.arg(
'--is_default',
metavar='<is_default>',
help=_("Defaults to the default securitygroup of the group"),
type=lambda v: strutils.bool_from_string(v, True),
default=True)
def do_securitygroup_update(cs, args):
"""
Update the specified security group.
"""
securitygroup = cs.securitygroups.update(args.gid,
args.securitygroup_id,
args.is_default)
d = securitygroup._info
print_dict(d)
@cliutils.arg(
'securitygroup_id',
metavar='<securitygroup_id>',
help=_("Securitygroup id"))
def do_securitygroup_delete(cs, args):
"""
Delete the specified security group.
"""
cs.securitygroups.delete(args.gid, args.securitygroup_id)
def do_network_list(cs, args):
"""
Print a list of all networks in the specified group.
"""
networks = cs.networks.list(args.gid)
fields = [
'network_id', 'name', 'is_admin', 'status'
]
print_list(networks, fields, sortby='network_id')
@cliutils.arg(
'network_id',
metavar='<network_id>',
help=_("network id"))
def do_network_show(cs, args):
"""
Show details about the given network.
"""
network = cs.networks.get(args.gid, args.network_id)
d = network._info
print_dict(d)
@cliutils.arg(
'cidr',
metavar='<cidr>',
help=_("CIDR of the new network"))
@cliutils.arg(
'--name',
metavar='<name>',
help=_("Name of the new network"))
@cliutils.arg(
'--is_admin',
metavar='<is_admin>',
help=_(""),
type=lambda v: strutils.bool_from_string(v, True),
default=False)
@cliutils.arg(
'--gateway_ip',
metavar='<gateway_ip>',
help=_("Gateway ip address of the new network"))
@cliutils.arg(
'--dns_nameserver',
metavar='<dns_nameserver>',
dest='dns_nameservers',
action='append',
help=_("DNS server for the new network (Can be repeated)"))
@cliutils.arg(
'--ext_router_id',
metavar='<ext_router_id>',
help=_("Router id the new network connects to"))
def do_network_create(cs, args):
"""
Create a network.
"""
network = cs.networks.create(args.gid,
args.cidr,
args.name,
args.is_admin,
args.gateway_ip,
args.dns_nameservers,
args.ext_router_id)
d = network._info
print_dict(d)
@cliutils.arg(
'network_id',
metavar='<network_id>',
help=_("network id"))
def do_network_delete(cs, args):
"""
Delete the specified network.
"""
cs.networks.delete(args.gid, args.network_id)
def do_process_list(cs, args):
"""
Print a list of all processes in the specified group.
"""
processes = cs.processes.list(args.gid)
fields = [
'pid', 'ppid', 'name', 'status'
]
print_list(processes, fields, sortby='pid')
@cliutils.arg(
'pid',
metavar='<pid>',
help=_("process ID"))
def do_process_show(cs, args):
"""
Show details about the given process.
"""
process = cs.processes.get(args.gid, args.pid)
d = process._info
print_process(d)
@cliutils.arg(
'--ppid',
metavar='<ppid>',
help=_("Parent process id of the new process"))
@cliutils.arg(
'--name',
metavar='<name>',
help=_("Name of the new process"))
@cliutils.arg(
'--nova_flavor_id',
metavar='<nova_flavor_id>',
help=_("Flavor id of the new process"))
@cliutils.arg(
'--glance_image_id',
metavar='<glance_image_id>',
help=_("Image id that registered on Glance of the new process"))
@cliutils.arg(
'--keypair_id',
metavar='<keypair_id>',
help=_("Keypair id of the new process uses"))
@cliutils.arg(
'--securitygroup_id',
metavar='<securitygroup_id>',
dest='securitygroup_ids',
action='append',
default=[],
help=_("Securitygroup id the new process belongs to (Can be repeated)"))
@cliutils.arg(
'--userdata',
metavar='<userdata>',
help=_("Userdata file to pass"))
@cliutils.arg(
'--args',
metavar='<key1=value1,key2=value2 or a file including key=value lines>',
help=_("Key-value pairs to be passed to metadata server"))
def do_process_create(cs, args):
"""
Create a new process.
"""
if args.userdata:
try:
userdata = open(args.userdata)
except IOError as e:
raise exceptions.CommandError(
_("Can't open '%s'") % args.userdata)
else:
userdata = None
if args.args:
if os.path.exists(args.args):
try:
f = open(args.args)
options = {}
for line in f:
k, v = line.split('=', 1)
options.update({k.strip(): v.strip()})
except IOError as e:
raise exceptions.CommandError(
_("Can't open '%s'") % args.args)
except ValueError:
raise exceptions.CommandError(
_("%s is not the format of key=value lines") % args.args)
else:
try:
options = _keyvalue_to_dict(args.args)
except argparse.ArgumentTypeError as e:
raise exceptions.CommandError(e)
else:
options = None
process = cs.processes.create(args.gid,
ppid=args.ppid,
name=args.name,
nova_flavor_id=args.nova_flavor_id,
glance_image_id=args.glance_image_id,
keypair_id=args.keypair_id,
securitygroup_ids=args.securitygroup_ids,
userdata=userdata,
args=options)
d = process._info
print_process(d)
@cliutils.arg(
'pid',
metavar='<pid>',
help=_("Process id"))
@cliutils.arg(
'--app_status',
metavar='<app_status>',
help=_("Application layer status of the process"))
def do_process_update(cs, args):
"""
Update the specified process.
"""
process = cs.processes.update(args.gid, args.pid, args.app_status)
d = process._info
print_process(d)
@cliutils.arg(
'pid',
metavar='<pid>',
help=_("Process id"))
def do_process_delete(cs, args):
"""
Delete the specified process.
"""
cs.processes.delete(args.gid, args.pid)
def do_proxy_show(cs, args):
"""
Show details about the given rack-proxy process.
"""
proxy = cs.proxy.get(args.gid)
d = proxy._info
print_process(d)
@cliutils.arg(
'--name',
metavar='<name>',
help=_("Name of the new process"))
@cliutils.arg(
'--nova_flavor_id',
metavar='<nova_flavor_id>',
help=_("Flavor id of the new process"))
@cliutils.arg(
'--glance_image_id',
metavar='<glance_image_id>',
help=_("Image id that registered on Glance of the new process"))
@cliutils.arg(
'--keypair_id',
metavar='<keypair_id>',
help=_("Keypair id of the new process uses"))
@cliutils.arg(
'--securitygroup_id',
metavar='<securitygroup_id>',
dest='securitygroup_ids',
action='append',
default=[],
help=_("Securitygroup id the new process belongs to (Can be repeated)"))
@cliutils.arg(
'--userdata',
metavar='<userdata>',
help=_("Userdata file to pass"))
@cliutils.arg(
'--args',
metavar='<key1=value1,key2=value2 or a file including key=value lines>',
help=_("Key-value pairs to be passed to metadata server"))
def do_proxy_create(cs, args):
"""
Create a new rack-proxy process.
"""
if args.userdata:
try:
userdata = open(args.userdata)
except IOError as e:
raise exceptions.CommandError(
_("Can't open '%s'") % args.userdata)
else:
userdata = None
if args.args:
if os.path.exists(args.args):
try:
f = open(args.args)
options = {}
for line in f:
k, v = line.split('=', 1)
options.update({k.strip(): v.strip()})
except IOError as e:
raise exceptions.CommandError(
_("Can't open '%s'") % args.args)
except ValueError:
raise exceptions.CommandError(
_("%s is not the format of key=value lines") % args.args)
else:
try:
options = _keyvalue_to_dict(args.args)
except argparse.ArgumentTypeError as e:
raise exceptions.CommandError(e)
else:
options = None
proxy = cs.proxy.create(args.gid,
name=args.name,
nova_flavor_id=args.nova_flavor_id,
glance_image_id=args.glance_image_id,
keypair_id=args.keypair_id,
securitygroup_ids=args.securitygroup_ids,
userdata=userdata,
args=options)
d = proxy._info
print_process(d)
@cliutils.arg(
'--shm_endpoint',
metavar='<shm_endpoint>',
help=_("Endpoint of the shared memory service"))
@cliutils.arg(
'--ipc_endpoint',
metavar='<ipc_endpoint>',
help=_("Endpoint of the IPC service"))
@cliutils.arg(
'--fs_endpoint',
metavar='<fs_endpoint>',
help=_("Endpoint of the file system service"))
@cliutils.arg(
'--app_status',
metavar='<app_status>',
help=_("Application layer status of the proxy"))
def do_proxy_update(cs, args):
"""
Update the specified rack-proxy process.
"""
proxy = cs.proxy.update(args.gid,
args.shm_endpoint,
args.ipc_endpoint,
args.fs_endpoint,
args.app_status)
d = proxy._info
print_process(d)
def print_process(d):
securitygroup_ids = d.get('securitygroup_ids')
if securitygroup_ids:
d['securitygroup_ids'] = ','.join(securitygroup_ids)
network_ids = d.get('network_ids')
if network_ids:
d['network_ids'] = ','.join(network_ids)
args = d.get('args')
if args:
s = ''
for k, v in args.items():
s += k + '=' + v + '\n'
d['args'] = s.rstrip('\n')
print_dict(d)
def print_list(objects, fields, sortby=None):
pt = prettytable.PrettyTable(fields)
pt.align = 'l'
for o in objects:
row = []
for f in fields:
row.append(getattr(o, f, ''))
pt.add_row(row)
print(pt.get_string(sortby=sortby))
def print_dict(d):
pt = prettytable.PrettyTable(['Property', 'Value'])
pt.align = 'l'
for k, v in sorted(d.items()):
pt.add_row([k, v])
print(pt.get_string())
@cliutils.arg(
'config',
metavar='<config-file>',
help=_("Configuration file included parameters of the new group"))
def do_group_init(cs, args):
"""
Create a group, a keypair, a security group, a network and
a rack-proxy based on the specified configuration file.
"""
config = ConfigParser()
config.read(args.config)
# Create a group
try:
name = config.get('group', 'name')
except NoOptionError:
raise exceptions.CommandError(_("Group name is required."))
try:
description = config.get('group', 'description')
except NoOptionError:
description = None
group = cs.groups.create(name, description)
d = group._info
print_dict(d)
gid = group.gid
# Create a keypair
try:
name = config.get('keypair', 'name')
except NoOptionError:
name = None
try:
is_default = config.get('keypair', 'is_default')
except NoOptionError:
is_default = False
keypair = cs.keypairs.create(gid, name, is_default)
d = keypair._info
print_dict(d)
# Create a securitygroup
try:
name = config.get('securitygroup', 'name')
except NoOptionError:
name = None
try:
is_default = config.get('securitygroup', 'is_default')
except NoOptionError:
is_default = False
try:
rules = config.get('securitygroup', 'rules').split()
for i in range(len(rules)):
rules[i] = _keyvalue_to_dict(rules[i])
except NoOptionError:
rules = []
except argparse.ArgumentTypeError as e:
raise exceptions.CommandError(
_("Could not create a securitygroup: "
"securitygroup rules are not valid formart: %s") % e.message)
securitygroup = cs.securitygroups.create(gid, name, is_default, rules)
d = securitygroup._info
print_dict(d)
# Create a network
try:
cidr = config.get('network', 'cidr')
except NoOptionError:
raise exceptions.CommandError(_("Network cidr is required."))
try:
name = config.get('network', 'name')
except NoOptionError:
name = None
try:
is_admin = config.get('network', 'is_admin')
except NoOptionError:
is_admin = False
try:
gateway_ip = config.get('network', 'gateway_ip')
except NoOptionError:
gateway_ip = None
try:
dns_nameservers = config.get('network', 'dns_nameservers').split()
except NoOptionError:
dns_nameservers = []
try:
ext_router_id = config.get('network', 'ext_router_id')
except NoOptionError:
ext_router_id = None
network = cs.networks.create(gid, cidr, name, is_admin,
gateway_ip, dns_nameservers, ext_router_id)
d = network._info
print_dict(d)
# Create a proxy
try:
name = config.get('proxy', 'name')
except NoOptionError:
name = None
try:
nova_flavor_id = config.get('proxy', 'nova_flavor_id')
except NoOptionError:
raise exceptions.CommandError(_("Flavor id is required."))
try:
glance_image_id = config.get('proxy', 'glance_image_id')
except NoOptionError:
raise exceptions.CommandError(_("Image id is required."))
keypair_id = keypair.keypair_id
securitygroup_ids = [securitygroup.securitygroup_id]
try:
userdata = config.get('proxy', 'userdata')
userdata = open(userdata)
except NoOptionError:
userdata = None
except IOError:
raise exceptions.CommandError(
_("Can't open %s.") % userdata)
try:
proxy_args = config.get('proxy', 'args')
proxy_args = _keyvalue_to_dict(proxy_args)
except NoOptionError:
proxy_args = None
except argparse.ArgumentTypeError as e:
raise exceptions.CommandError(e)
proxy = cs.proxy.create(gid, name=name,
nova_flavor_id=nova_flavor_id,
glance_image_id=glance_image_id,
keypair_id=keypair_id,
securitygroup_ids=securitygroup_ids,
userdata=userdata,
args=proxy_args)
d = proxy._info
print_process(d)
result_dict = {
"gid": gid,
"keypair_id": keypair_id,
"securitygroup_id": securitygroup.securitygroup_id,
"network_id": network.network_id,
"proxy pid": proxy.pid
}
print_dict(result_dict)

View File

@ -1,3 +1,4 @@
pbr>=0.10.8
requests>=1.1.0
redis>=2.10.3
Babel>=1.3
@ -9,3 +10,4 @@ PrettyTable>=0.7,<0.8
websocket-client>=0.16.0
python-keystoneclient>=0.11.2
pika>=0.9.14
cliff>=1.9.0

View File

@ -25,3 +25,35 @@ packages =
[entry_points]
console_scripts =
rack = rackclient.shell:main
rack.command =
group-list = rackclient.v1.command.groups:ListGroups
group-show = rackclient.v1.command.groups:ShowGroup
group-create = rackclient.v1.command.groups:CreateGroup
group-update = rackclient.v1.command.groups:UpdateGroup
group-delete = rackclient.v1.command.groups:DeleteGroup
group-init = rackclient.v1.command.groups:InitGroup
keypair-list = rackclient.v1.command.keypairs:ListKeypairs
keypair-show = rackclient.v1.command.keypairs:ShowKeypair
keypair-create = rackclient.v1.command.keypairs:CreateKeypair
keypair-update = rackclient.v1.command.keypairs:UpdateKeypair
keypair-delete = rackclient.v1.command.keypairs:DeleteKeypair
securitygroup-list = rackclient.v1.command.securitygroups:ListSecuritygroups
securitygroup-show = rackclient.v1.command.securitygroups:ShowSecuritygroup
securitygroup-create = rackclient.v1.command.securitygroups:CreateSecuritygroup
securitygroup-update = rackclient.v1.command.securitygroups:UpdateSecuritygroup
securitygroup-delete = rackclient.v1.command.securitygroups:DeleteSecuritygroup
network-list = rackclient.v1.command.networks:ListNetworks
network-show = rackclient.v1.command.networks:ShowNetwork
network-create = rackclient.v1.command.networks:CreateNetwork
network-delete = rackclient.v1.command.networks:DeleteNetwork
proxy-show = rackclient.v1.command.proxy:ShowProxy
proxy-create = rackclient.v1.command.proxy:CreateProxy
proxy-update = rackclient.v1.command.proxy:UpdateProxy
ps = rackclient.v1.command.processes:PS
show = rackclient.v1.command.processes:Show
boot = rackclient.v1.command.processes:Boot
kill = rackclient.v1.command.processes:Kill
# applications
montecarlo = rackclient.v1.command.montecarlo:Montecarlo