Source code for CloudDB

#!/usr/bin/python

# Copyright 2012 Rackspace Hosting {{{
#
# Contact - Nate House nathan.house@rackspace.com
#
#   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 Cloud import *

#Setup logger.  Root logger config in _Globals.py
log = logging.getLogger(__name__)
#log.setLevel(logging.DEBUG)

[docs]class CloudDB(Cloud): """ A class that handles actions to Cloud Databases via public methods listed. """ #properties + constructor{{{ service = "Databases" flavors = { } #flavorId and href def __init__(self,*args): super(CloudDB,self).__init__(*args) self._getFlavors() #}}} #DB Instances{{{
[docs] def createInstance(self, flavor, size, name, db=None, user=None): """ Creates a mysql instance using the provided flavor id and size(1-50 GB) for allocating databases to. Arguments: flavor -- Instance flavor size(int) size -- Instance storage size(int) name -- Instance name(string) db -- List of databses with optional keys(list) name -- Database name(string) character_set -- Default utf8(string) collate -- Rules default is utf8_general_ci(string) user -- List of users with optional keys(list) name -- User name(string) password -- User pass(string) databases -- List of databases w/ name key(list) Returns: result -- JSON response converted to python Dict """ self.baseURL('instances') if int(size) not in range(1,51): log.warning("Size not in range on setInstance") elif int(flavor) not in self.flavors: log.warning("Flavor key not in dictionary on setInstance") log.info("createInstance assigning required vars") params = { 'instance': { 'flavorRef': self.flavors[int(flavor)], 'volume': { 'size': str(size) }, 'name': name } } log.info("createInstance assigning optional vars") if db is not None: params['instance']['databases'] = db if user is not None: params['instance']['users'] = user log.info("createInstance POST call to %s with data - %s" % (self.endpoint, str(params))) return apiRequest(self, self.endpoint, "POST", data=params, returnResults=True)
[docs] def getInstance(self, iId=None): """ Lists mysql instance details. Either all or one with the provided instance id Arguments: iId -- Instance id(str) Returns: result -- JSON response converted to python Dict """ log.info("getInstance assigning optional vars") if iId is not None: self.baseURL('instances/'+str(iId)) else: self.baseURL('instances') log.info("getInstance GET call to %s" % self.endpoint) return apiRequest(self, self.endpoint, "GET", returnResults=True)
[docs] def deleteInstance(self, iId): """ Deletes mysql instance using provided instance id Arguments: iId -- Instance id(str) Returns: status code """ self.baseURL('instances/'+str(iId)) log.info("deleteInstance DELETE call to %s" % self.endpoint) return apiRequest(self, self.endpoint, "DELETE")
[docs] def getRoot(self, iId, check=False): """ Enable and get root credentials for mysql instance using provided instance id Or if check=True, return status of root enabled on the instance. Arguments: iId -- Instance id(str) Returns: JSON results as a python dict """ self.baseURL('instances/'+str(iId)+'/root') if not check: log.info("getRoot POST to %s" % self.endpoint) return apiRequest(self, self.endpoint, "POST", returnResults=True) else: log.info("getRoot GET to %s" % self.endpoint) return apiRequest(self, self.endpoint, "GET", returnResults=True) #}}} #DB Instance Actions{{{
[docs] def setAction(self, iId, reboot=False, flavor=False, volume=False): """ Perfom action on instance such as reboot mysql or resize flavor/volume. Arguments: iId -- Instance id(string) reboot -- Boolean flavor -- Flavor id(int) volume -- Storage size in GB(int) Returns: status code """ self.baseURL('instances/'+str(iId)+'/action') if not reboot and not flavor and not volume: log.warning("setAction need reboot, flavor or volume argument") elif flavor is not False and int(flavor) not in self.flavors: log.warning("setAction flavor key %s not in dictionary" % str(flavor)) elif volume is not False and int(volume) not in range(1,51): log.warning("setAction volume %s is not within range 1-50" % str(volume)) else: log.warning("setAction needs reboot, flavor or volume arg set") data = {} if reboot: data = { 'restart': { } } elif flavor: data = { 'resize': { 'flavorRef': self.flavors[int(flavor)] } } elif volume: data = { 'resize': { 'volume': { 'size': int(volume) } } } log.info("setInstance POST to %s with data - %s" % (self.endpoint, str(data))) return apiRequest(self, self.endpoint, "POST", data) #}}} #Databases{{{
[docs] def createDatabase(self, iId, name, charset=None, collate=None): """ Create new database within an instance id given the provided name Arguments: iId -- Instance id(str) name -- Database name(str) charset -- Default utf8(string)(optional) collate -- Rules default is utf8_general_ci(string)(optional) Returns: status code """ self.baseURL('instances/'+str(iId)+'/databases') log.info('createDatabase assigning required vars') data = { 'databases': [ { 'name':str(name) } ] } log.info('createDatabase assigning optional vars') if charset is not None: data['databases'][0]['character_set'] = charset if collate is not None: data['databases'][0]['collate'] = collate log.info("createDatabase POST to %s with data - %s" % (self.endpoint, str(data))) return apiRequest(self, self.endpoint, "POST", data)
[docs] def getDatabases(self, iId): """ Lists databases within an instance Arguments: id -- Instance id(str) Returns: JSON results as python dict """ self.baseURL('instances/'+str(iId)+'/databases') log.info("getDatabases GET to %s" % self.endpoint) return apiRequest(self, self.endpoint, "GET", returnResults=True)
[docs] def deleteDatabase(self, iId, name): """ Deletes specified database name within specified instance id Arguments: id -- Instance id(str) name -- Instance name(str) Returns: status code """ self.baseURL('instances/'+str(iId)+'/databases/'+name) log.info("deleteDatabase DELETE to %s" % self.endpoint) return apiRequest(self, self.endpoint, "DELETE") #}}} #Users{{{
[docs] def createUser(self, iId, name, password, databases=None): """ Creates a user for the specified instance, optionally to specific databases with specified name and pass. Arguments: id -- name -- Name of the user for the database. password -- User password for database access. databases -- Optional(list of database names) Returns: status code """ self.baseURL('instances/'+str(iId)+'/users') log.info('createUser assigning required vars') data = { 'users': [ { 'name':name, 'password':password } ] } if databases is not None: log.info('createUser assigning optional vars') data['users'][0]['databases'] = databases log.info('createUser POST to %s with data - %s' % (self.endpoint, str(data))) return apiRequest(self, self.endpoint, "POST", data)
[docs] def getUsers(self, iId): """ Lists users associated with a specific instance Arguments: iId -- Instance id(string) Returns: JSON results as a python dict """ self.baseURL('instances/'+str(iId)+'/users') log.info("getUsers GET to %s" % self.endpoint) return apiRequest(self, self.endpoint, "GET", returnResults=True)
[docs] def getUser(self, iId, name): """ Lists a specified user associated with a specified instance Arguments: iId -- Instance id(string) name -- User name(string) Returns: status code """ self.baseURL('instances/'+str(iId)+'/users/'+name) log.info("getUser GET to %s" % self.endpoint) return apiRequest(self, self.endpoint, "GET", returnResults=True)
[docs] def getUserAccess(self, iId, name): """ Lists a specified user associated with a specified instance Arguments: iId -- Instance id(string) name -- User name(string) Returns: status code """ self.baseURL('instances/'+str(iId)+'/users/'+name+'/databases') log.info("getUserAccess GET to %s" % self.endpoint) return apiRequest(self, self.endpoint, "GET", returnResults=True)
[docs] def grantUserAccess(self, iId, username, dbName): """ Grants access for the specified user to one or more databases for the specified instance. Arguments: iId -- Instance id(string) username -- User name (string) dbName -- New user password for database access Returns: status code """ self.baseURL('instances/'+str(iId)+'/users/'+username+'/databases') log.info('grantUserAccess assigning required vars username:%s and dbName:%s' % (username, dbName)) data = { 'databases': [ { 'name':dbName } ] } log.info('grantUserAccess PUT to %s with data - %s' % (self.endpoint, str(data))) return apiRequest(self, self.endpoint, "PUT", data)
[docs] def revokeUserAccess(self, iId, username, dbName): """ Revokes access for the specified user to one or more databases for the specified instance. Arguments: iId -- Instance id(string) username -- User name (string) dbName -- New user password for database access Returns: status code """ self.baseURL('instances/'+str(iId)+'/users/'+username+'/databases/'+dbName) log.info('revokeUserAccess DELETE to %s ' % (self.endpoint)) return apiRequest(self, self.endpoint, "DELETE")
[docs] def deleteUser(self, iId, name): """ Deletes a specified user from a specified instance Arguments: iId -- Instance id(string) name -- User name(string) Returns: status code """ self.baseURL('instances/'+str(iId)+'/users/'+name) log.info("deleteUser DELETE to %s" % self.endpoint) return apiRequest(self, self.endpoint, "DELETE")
[docs] def changePass(self, iId, name, password): """ Changes the MySQL password of the specified user. Arguments: iId -- Instance id(string) name -- User name (string) password -- New user password for database access Returns: status code """ self.baseURL('instances/'+str(iId)+'/users') log.info('changePass assigning required vars') data = { 'users': [ { 'name':name, 'password':password } ] } log.info('changePass PUT to %s with data - %s' % (self.endpoint, str(data))) return apiRequest(self, self.endpoint, "PUT", data) #}}} #Flavors{{{
[docs] def getFlavor(self, fId=None): """ Lists info about flavor(s). Can specify an id for detailed info on flavor Arguments: fId -- Flavor id(int)(optional) Returns: JSON results as python dict """ if fId is not None: self.baseURL('flavors/'+str(fId)) else: self.baseURL('flavors') log.info("getFlavor GET to %s" % self.endpoint) return apiRequest(self, self.endpoint, "GET", returnResults=True)
def _getFlavors(self): """ Updates self.flavors dict to have an index(int) based on flavor id with href url value Called on init """ self.baseURL('flavors') try: data = apiRequest(self, self.endpoint, "GET", returnResults=True) fdata = loads(dumps(data['flavors'])) for flavor in fdata: self.flavors[flavor['id']] = flavor['links'][0]['href'] except: log.warning("Unhandled exception caught in CloudDB._getFlavors()") #}}} #eof