diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..e1f6c479 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*.pyc +python_reddwarfclient.egg* diff --git a/reddwarfclient/cli.py b/reddwarfclient/cli.py new file mode 100644 index 00000000..dcb98709 --- /dev/null +++ b/reddwarfclient/cli.py @@ -0,0 +1,313 @@ +#!/usr/bin/env python + +# Copyright 2011 OpenStack LLC +# +# 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. + +""" +Reddwarf Command line tool +""" + +import json +import optparse +import os +import sys + + +# If ../reddwarf/__init__.py exists, add ../ to Python search path, so that +# it will override what happens to be installed in /usr/(local/)lib/python... +possible_topdir = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]), + os.pardir, + os.pardir)) +if os.path.exists(os.path.join(possible_topdir, 'reddwarfclient', + '__init__.py')): + sys.path.insert(0, possible_topdir) +if os.path.exists(os.path.join(possible_topdir, 'nova', '__init__.py')): + sys.path.insert(0, possible_topdir) + + +from reddwarfclient import common + + +oparser = None + + +def _pretty_print(info): + print json.dumps(info, sort_keys=True, indent=4) + + +class InstanceCommands(object): + """Commands to perform various instances operations and actions""" + + def __init__(self): + pass + + def create(self, name, volume_size, + flavorRef="http://localhost:8775/v1.0/flavors/1"): + """Create a new instance""" + dbaas = common.get_client() + volume = {"size": volume_size} + try: + result = dbaas.instances.create(name, flavorRef, volume) + _pretty_print(result._info) + except: + print sys.exc_info()[1] + + def delete(self, id): + """Delete the specified instance""" + dbaas = common.get_client() + try: + result = dbaas.instances.delete(id) + if result: + print result + except: + print sys.exc_info()[1] + + def get(self, id): + """Get details for the specified instance""" + dbaas = common.get_client() + try: + _pretty_print(dbaas.instances.get(id)._info) + except: + print sys.exc_info()[1] + + def list(self): + """List all instances for account""" + dbaas = common.get_client() + try: + for instance in dbaas.instances.list(): + _pretty_print(instance._info) + except: + print sys.exc_info()[1] + + def resize_volume(self, id, size): + """Resize an instance volume""" + dbaas = common.get_client() + try: + result = dbaas.instances.resize_volume(id, size) + if result: + print result + except: + print sys.exc_info()[1] + + def resize_instance(self, id, flavor_id): + """Resize an instance flavor""" + dbaas = common.get_client() + try: + result = dbaas.instances.resize_instance(id, flavor_id) + if result: + print result + except: + print sys.exc_info()[1] + + def restart(self, id): + """Restart the database""" + dbaas = common.get_client() + try: + result = dbaas.instances.restart(id) + if result: + print result + except: + print sys.exc_info()[1] + + +class FlavorsCommands(object): + """Commands for listing Flavors""" + + def __init__(self): + pass + + def list(self): + """List the available flavors""" + dbaas = common.get_client() + try: + for flavor in dbaas.flavors.list(): + _pretty_print(flavor._info) + except: + print sys.exc_info()[1] + + +class DatabaseCommands(object): + """Database CRUD operations on an instance""" + + def __init__(self): + pass + + def create(self, id, dbname): + """Create a database""" + dbaas = common.get_client() + try: + databases = [{'name': dbname}] + dbaas.databases.create(id, databases) + except: + print sys.exc_info()[1] + + def delete(self, id, dbname): + """Delete a database""" + dbaas = common.get_client() + try: + dbaas.databases.delete(id, dbname) + except: + print sys.exc_info()[1] + + def list(self, id): + """List the databases""" + dbaas = common.get_client() + try: + for database in dbaas.databases.list(id): + _pretty_print(database._info) + except: + print sys.exc_info()[1] + + +class UserCommands(object): + """User CRUD operations on an instance""" + + def __init__(self): + pass + + def create(self, id, username, password, dbname, *args): + """Create a user in instance, with access to one or more databases""" + dbaas = common.get_client() + try: + databases = [{'name': dbname}] + [databases.append({"name": db}) for db in args] + users = [{'name': username, 'password': password, + 'databases': databases}] + dbaas.users.create(id, users) + except: + print sys.exc_info()[1] + + def delete(self, id, user): + """Delete the specified user""" + dbaas = common.get_client() + try: + dbaas.users.delete(id, user) + except: + print sys.exc_info()[1] + + def list(self, id): + """List all the users for an instance""" + dbaas = common.get_client() + try: + for user in dbaas.users.list(id): + _pretty_print(user._info) + except: + print sys.exc_info()[1] + + +class RootCommands(object): + """Root user related operations on an instance""" + + def __init__(self): + pass + + def create(self, id): + """Enable the instance's root user.""" + dbaas = common.get_client() + try: + user, password = dbaas.root.create(id) + print "User:\t\t%s\nPassword:\t%s" % (user, password) + except: + print sys.exc_info()[1] + + def enabled(self, id): + """Check the instance for root access""" + dbaas = common.get_client() + try: + _pretty_print(dbaas.root.is_root_enabled(id)) + except: + print sys.exc_info()[1] + + +class VersionCommands(object): + """List available versions""" + + def __init__(self): + pass + + def list(self, url): + """List all the supported versions""" + dbaas = common.get_client() + try: + versions = dbaas.versions.index(url) + for version in versions: + _pretty_print(version._info) + except: + print sys.exc_info()[1] + + +def config_options(): + global oparser + oparser.add_option("-u", "--url", default="http://localhost:5000/v1.1", + help="Auth API endpoint URL with port and version. \ + Default: http://localhost:5000/v1.1") + + +COMMANDS = {'auth': common.Auth, + 'instance': InstanceCommands, + 'flavor': FlavorsCommands, + 'database': DatabaseCommands, + 'user': UserCommands, + 'root': RootCommands, + 'version': VersionCommands} + + +def main(): + # Parse arguments + global oparser + oparser = optparse.OptionParser("%prog [options] ", + version='1.0') + config_options() + (options, args) = oparser.parse_args() + + if not args: + common.print_commands(COMMANDS) + + # Pop the command and check if it's in the known commands + cmd = args.pop(0) + if cmd in COMMANDS: + fn = COMMANDS.get(cmd) + command_object = fn() + + # Get a list of supported actions for the command + actions = common.methods_of(command_object) + + if len(args) < 1: + common.print_actions(cmd, actions) + + # Check for a valid action and perform that action + action = args.pop(0) + if action in actions: + fn = actions.get(action) + + try: + fn(*args) + sys.exit(0) + except TypeError as err: + print "Possible wrong number of arguments supplied." + print "%s %s: %s" % (cmd, action, fn.__doc__) + print "\t\t", [fn.func_code.co_varnames[i] for i in + range(fn.func_code.co_argcount)] + print "ERROR: %s" % err + except Exception: + print "Command failed, please check the log for more info." + raise + else: + common.print_actions(cmd, actions) + else: + common.print_commands(COMMANDS) + + +if __name__ == '__main__': + main() diff --git a/setup.py b/setup.py new file mode 100644 index 00000000..3bd7accc --- /dev/null +++ b/setup.py @@ -0,0 +1,55 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2010 United States Government as represented by the +# Administrator of the National Aeronautics and Space Administration. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import os +import setuptools +import sys + + +requirements = ["python-novaclient"] + + +def read_file(file_name): + return open(os.path.join(os.path.dirname(__file__), file_name)).read() + + +setuptools.setup( + name="python-reddwarfclient", + version="2012.3", + author="Rackspace", + description="Rich client bindings for Reddwarf REST API.", + long_description=read_file("README.rst"), + license="Apache License, Version 2.0", + url="https://github.com/openstack/python-reddwarfclient", + packages=["reddwarfclient"], + install_requires=requirements, + tests_require=["nose", "mock"], + test_suite="nose.collector", + classifiers=[ + "Development Status :: 5 - Production/Stable", + "Environment :: Console", + "Intended Audience :: Developers", + "Intended Audience :: Information Technology", + "License :: OSI Approved :: Apache Software License", + "Operating System :: OS Independent", + "Programming Language :: Python" + ], + entry_points={ + "console_scripts": ["reddwarf-cli = reddwarfclient.cli:main"] + } +)