 bb6a4da316
			
		
	
	bb6a4da316
	
	
	
		
			
			* Corresponds to change made in reddwarf-integration that changes the service name + type in keystone. fixes bug# 1122515 Change-Id: I28a366662bfe9816a926708fc376afdf29f00de1
		
			
				
	
	
		
			396 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			396 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			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.
 | |
| 
 | |
| import copy
 | |
| import json
 | |
| import optparse
 | |
| import os
 | |
| import pickle
 | |
| import sys
 | |
| 
 | |
| from reddwarfclient import client
 | |
| from reddwarfclient.xml import ReddwarfXmlClient
 | |
| from reddwarfclient import exceptions
 | |
| 
 | |
| 
 | |
| def methods_of(obj):
 | |
|     """Get all callable methods of an object that don't start with underscore
 | |
|     returns a list of tuples of the form (method_name, method)"""
 | |
|     result = {}
 | |
|     for i in dir(obj):
 | |
|         if callable(getattr(obj, i)) and not i.startswith('_'):
 | |
|             result[i] = getattr(obj, i)
 | |
|     return result
 | |
| 
 | |
| 
 | |
| def check_for_exceptions(resp, body):
 | |
|     if resp.status in (400, 422, 500):
 | |
|             raise exceptions.from_response(resp, body)
 | |
| 
 | |
| 
 | |
| def print_actions(cmd, actions):
 | |
|     """Print help for the command with list of options and description"""
 | |
|     print ("Available actions for '%s' cmd:") % cmd
 | |
|     for k, v in actions.iteritems():
 | |
|         print "\t%-20s%s" % (k, v.__doc__)
 | |
|     sys.exit(2)
 | |
| 
 | |
| 
 | |
| def print_commands(commands):
 | |
|     """Print the list of available commands and description"""
 | |
| 
 | |
|     print "Available commands"
 | |
|     for k, v in commands.iteritems():
 | |
|         print "\t%-20s%s" % (k, v.__doc__)
 | |
|     sys.exit(2)
 | |
| 
 | |
| 
 | |
| def limit_url(url, limit=None, marker=None):
 | |
|     if not limit and not marker:
 | |
|         return url
 | |
|     query = []
 | |
|     if marker:
 | |
|         query.append("marker=%s" % marker)
 | |
|     if limit:
 | |
|         query.append("limit=%s" % limit)
 | |
|     query = '?' + '&'.join(query)
 | |
|     return url + query
 | |
| 
 | |
| 
 | |
| class CliOptions(object):
 | |
|     """A token object containing the user, apikey and token which
 | |
|        is pickleable."""
 | |
| 
 | |
|     APITOKEN = os.path.expanduser("~/.apitoken")
 | |
| 
 | |
|     DEFAULT_VALUES = {
 | |
|         'username': None,
 | |
|         'apikey': None,
 | |
|         'tenant_id': None,
 | |
|         'auth_url': None,
 | |
|         'auth_type': 'keystone',
 | |
|         'service_type': 'database',
 | |
|         'service_name': 'reddwarf',
 | |
|         'region': 'RegionOne',
 | |
|         'service_url': None,
 | |
|         'insecure': False,
 | |
|         'verbose': False,
 | |
|         'debug': False,
 | |
|         'token': None,
 | |
|         'xml': None,
 | |
|     }
 | |
| 
 | |
|     def __init__(self, **kwargs):
 | |
|         for key, value in self.DEFAULT_VALUES.items():
 | |
|             setattr(self, key, value)
 | |
| 
 | |
|     @classmethod
 | |
|     def default(cls):
 | |
|         kwargs = copy.deepcopy(cls.DEFAULT_VALUES)
 | |
|         return cls(**kwargs)
 | |
| 
 | |
|     @classmethod
 | |
|     def load_from_file(cls):
 | |
|         try:
 | |
|             with open(cls.APITOKEN, 'rb') as token:
 | |
|                 return pickle.load(token)
 | |
|         except IOError:
 | |
|             pass  # File probably not found.
 | |
|         except:
 | |
|             print("ERROR: Token file found at %s was corrupt." % cls.APITOKEN)
 | |
|         return cls.default()
 | |
| 
 | |
|     @classmethod
 | |
|     def save_from_instance_fields(cls, instance):
 | |
|         apitoken = cls.default()
 | |
|         for key, default_value in cls.DEFAULT_VALUES.items():
 | |
|             final_value = getattr(instance, key, default_value)
 | |
|             setattr(apitoken, key, final_value)
 | |
|         with open(cls.APITOKEN, 'wb') as token:
 | |
|             pickle.dump(apitoken, token, protocol=2)
 | |
| 
 | |
|     @classmethod
 | |
|     def create_optparser(cls, load_file):
 | |
|         oparser = optparse.OptionParser(
 | |
|             usage="%prog [options] <cmd> <action> <args>",
 | |
|             version='1.0', conflict_handler='resolve')
 | |
|         if load_file:
 | |
|             file = cls.load_from_file()
 | |
|         else:
 | |
|             file = cls.default()
 | |
| 
 | |
|         def add_option(*args, **kwargs):
 | |
|             if len(args) == 1:
 | |
|                 name = args[0]
 | |
|             else:
 | |
|                 name = args[1]
 | |
|             kwargs['default'] = getattr(file, name, cls.DEFAULT_VALUES[name])
 | |
|             oparser.add_option("--%s" % name, **kwargs)
 | |
| 
 | |
|         add_option("verbose", action="store_true",
 | |
|                    help="Show equivalent curl statement along "
 | |
|             "with actual HTTP communication.")
 | |
|         add_option("debug", action="store_true",
 | |
|                    help="Show the stack trace on errors.")
 | |
|         add_option("auth_url", help="Auth API endpoint URL with port and "
 | |
|             "version. Default: http://localhost:5000/v2.0")
 | |
|         add_option("username", help="Login username")
 | |
|         add_option("apikey", help="Api key")
 | |
|         add_option("tenant_id",
 | |
|                    help="Tenant Id associated with the account")
 | |
|         add_option("auth_type",
 | |
|             help="Auth type to support different auth environments, \
 | |
|                                 Supported values are 'keystone', 'rax'.")
 | |
|         add_option("service_type",
 | |
|             help="Service type is a name associated for the catalog")
 | |
|         add_option("service_name",
 | |
|             help="Service name as provided in the service catalog")
 | |
|         add_option("service_url",
 | |
|             help="Service endpoint to use if the catalog doesn't have one.")
 | |
|         add_option("region", help="Region the service is located in")
 | |
|         add_option("insecure", action="store_true",
 | |
|                    help="Run in insecure mode for https endpoints.")
 | |
|         add_option("token", help="Token from a prior login.")
 | |
|         add_option("xml", action="store_true", help="Changes format to XML.")
 | |
| 
 | |
|         oparser.add_option("--secure", action="store_false", dest="insecure",
 | |
|                    help="Run in insecure mode for https endpoints.")
 | |
|         oparser.add_option("--json", action="store_false", dest="xml",
 | |
|                    help="Changes format to JSON.")
 | |
|         oparser.add_option("--terse", action="store_false", dest="verbose",
 | |
|                    help="Toggles verbose mode off.")
 | |
|         oparser.add_option("--hide-debug", action="store_false", dest="debug",
 | |
|                    help="Toggles debug mode off.")
 | |
|         return oparser
 | |
| 
 | |
| 
 | |
| class ArgumentRequired(Exception):
 | |
|     def __init__(self, param):
 | |
|         self.param = param
 | |
| 
 | |
|     def __str__(self):
 | |
|         return 'Argument "--%s" required.' % self.param
 | |
| 
 | |
| 
 | |
| class CommandsBase(object):
 | |
|     params = []
 | |
| 
 | |
|     def __init__(self, parser):
 | |
|         self._parse_options(parser)
 | |
| 
 | |
|     def _get_client(self):
 | |
|         """Creates the all important client object."""
 | |
|         try:
 | |
|             if self.xml:
 | |
|                 client_cls = ReddwarfXmlClient
 | |
|             else:
 | |
|                 client_cls = client.ReddwarfHTTPClient
 | |
|             if self.verbose:
 | |
|                 client.log_to_streamhandler(sys.stdout)
 | |
|                 client.RDC_PP = True
 | |
|             return client.Dbaas(self.username, self.apikey, self.tenant_id,
 | |
|                           auth_url=self.auth_url,
 | |
|                           auth_strategy=self.auth_type,
 | |
|                           service_type=self.service_type,
 | |
|                           service_name=self.service_name,
 | |
|                           region_name=self.region,
 | |
|                           service_url=self.service_url,
 | |
|                           insecure=self.insecure,
 | |
|                           client_cls=client_cls)
 | |
|         except:
 | |
|             if self.debug:
 | |
|                 raise
 | |
|             print sys.exc_info()[1]
 | |
| 
 | |
|     def _safe_exec(self, func, *args, **kwargs):
 | |
|         if not self.debug:
 | |
|             try:
 | |
|                 return func(*args, **kwargs)
 | |
|             except:
 | |
|                 print(sys.exc_info()[1])
 | |
|                 return None
 | |
|         else:
 | |
|             return func(*args, **kwargs)
 | |
| 
 | |
|     @classmethod
 | |
|     def _prepare_parser(cls, parser):
 | |
|         for param in cls.params:
 | |
|             parser.add_option("--%s" % param)
 | |
| 
 | |
|     def _parse_options(self, parser):
 | |
|         opts, args = parser.parse_args()
 | |
|         for param in opts.__dict__:
 | |
|             value = getattr(opts, param)
 | |
|             setattr(self, param, value)
 | |
| 
 | |
|     def _require(self, *params):
 | |
|         for param in params:
 | |
|             if not hasattr(self, param):
 | |
|                 raise ArgumentRequired(param)
 | |
|             if not getattr(self, param):
 | |
|                 raise ArgumentRequired(param)
 | |
| 
 | |
|     def _make_list(self, *params):
 | |
|         # Convert the listed params to lists.
 | |
|         for param in params:
 | |
|             raw = getattr(self, param)
 | |
|             if isinstance(raw, list):
 | |
|                 return
 | |
|             raw = [item.strip() for item in raw.split(',')]
 | |
|             setattr(self, param, raw)
 | |
| 
 | |
|     def _pretty_print(self, func, *args, **kwargs):
 | |
|         if self.verbose:
 | |
|             self._safe_exec(func, *args, **kwargs)
 | |
|             return  # Skip this, since the verbose stuff will show up anyway.
 | |
| 
 | |
|         def wrapped_func():
 | |
|             result = func(*args, **kwargs)
 | |
|             if result:
 | |
|                 print(json.dumps(result._info, sort_keys=True, indent=4))
 | |
|             else:
 | |
|                 print("OK")
 | |
|         self._safe_exec(wrapped_func)
 | |
| 
 | |
|     def _dumps(self, item):
 | |
|         return json.dumps(item, sort_keys=True, indent=4)
 | |
| 
 | |
|     def _pretty_list(self, func, *args, **kwargs):
 | |
|         result = self._safe_exec(func, *args, **kwargs)
 | |
|         if self.verbose:
 | |
|             return
 | |
|         if result and len(result) > 0:
 | |
|             for item in result:
 | |
|                 print(self._dumps(item._info))
 | |
|         else:
 | |
|             print("OK")
 | |
| 
 | |
|     def _pretty_paged(self, func, *args, **kwargs):
 | |
|         try:
 | |
|             limit = self.limit
 | |
|             if limit:
 | |
|                 limit = int(limit, 10)
 | |
|             result = func(*args, limit=limit, marker=self.marker, **kwargs)
 | |
|             if self.verbose:
 | |
|                 return  # Verbose already shows the output, so skip this.
 | |
|             if result and len(result) > 0:
 | |
|                 for item in result:
 | |
|                     print self._dumps(item._info)
 | |
|                 if result.links:
 | |
|                     print("Links:")
 | |
|                     for link in result.links:
 | |
|                         print self._dumps((link))
 | |
|             else:
 | |
|                 print("OK")
 | |
|         except:
 | |
|             if self.debug:
 | |
|                 raise
 | |
|             print sys.exc_info()[1]
 | |
| 
 | |
| 
 | |
| class Auth(CommandsBase):
 | |
|     """Authenticate with your username and api key"""
 | |
|     params = [
 | |
|               'apikey',
 | |
|               'auth_strategy',
 | |
|               'auth_type',
 | |
|               'auth_url',
 | |
|               'options',
 | |
|               'region',
 | |
|               'service_name',
 | |
|               'service_type',
 | |
|               'service_url',
 | |
|               'tenant_id',
 | |
|               'username',
 | |
|              ]
 | |
| 
 | |
|     def __init__(self, parser):
 | |
|         super(Auth, self).__init__(parser)
 | |
|         self.dbaas = None
 | |
| 
 | |
|     def login(self):
 | |
|         """Login to retrieve an auth token to use for other api calls"""
 | |
|         self._require('username', 'apikey', 'tenant_id', 'auth_url')
 | |
|         try:
 | |
|             self.dbaas = self._get_client()
 | |
|             self.dbaas.authenticate()
 | |
|             self.token = self.dbaas.client.auth_token
 | |
|             self.service_url = self.dbaas.client.service_url
 | |
|             CliOptions.save_from_instance_fields(self)
 | |
|             print("Token aquired! Saving to %s..." % CliOptions.APITOKEN)
 | |
|             print("    service_url = %s" % self.service_url)
 | |
|             print("    token       = %s" % self.token)
 | |
|         except:
 | |
|             if self.debug:
 | |
|                 raise
 | |
|             print sys.exc_info()[1]
 | |
| 
 | |
| 
 | |
| class AuthedCommandsBase(CommandsBase):
 | |
|     """Commands that work only with an authicated client."""
 | |
| 
 | |
|     def __init__(self, parser):
 | |
|         """Makes sure a token is available somehow and logs in."""
 | |
|         super(AuthedCommandsBase, self).__init__(parser)
 | |
|         try:
 | |
|             self._require('token')
 | |
|         except ArgumentRequired:
 | |
|             if self.debug:
 | |
|                 raise
 | |
|             print('No token argument supplied. Use the "auth login" command '
 | |
|                   'to log in and get a token.\n')
 | |
|             sys.exit(1)
 | |
|         try:
 | |
|             self._require('service_url')
 | |
|         except ArgumentRequired:
 | |
|             if self.debug:
 | |
|                 raise
 | |
|             print('No service_url given.\n')
 | |
|             sys.exit(1)
 | |
|         self.dbaas = self._get_client()
 | |
|         # Actually set the token to avoid a re-auth.
 | |
|         self.dbaas.client.auth_token = self.token
 | |
|         self.dbaas.client.authenticate_with_token(self.token, self.service_url)
 | |
| 
 | |
| 
 | |
| class Paginated(object):
 | |
|     """ Pretends to be a list if you iterate over it, but also keeps a
 | |
|         next property you can use to get the next page of data. """
 | |
| 
 | |
|     def __init__(self, items=[], next_marker=None, links=[]):
 | |
|         self.items = items
 | |
|         self.next = next_marker
 | |
|         self.links = links
 | |
| 
 | |
|     def __len__(self):
 | |
|         return len(self.items)
 | |
| 
 | |
|     def __iter__(self):
 | |
|         return self.items.__iter__()
 | |
| 
 | |
|     def __getitem__(self, key):
 | |
|         return self.items[key]
 | |
| 
 | |
|     def __setitem__(self, key, value):
 | |
|         self.items[key] = value
 | |
| 
 | |
|     def __delitem__(self, key):
 | |
|         del self.items[key]
 | |
| 
 | |
|     def __reversed__(self):
 | |
|         return reversed(self.items)
 | |
| 
 | |
|     def __contains__(self, needle):
 | |
|         return needle in self.items
 |