| # Licensed to the Apache Software Foundation (ASF) under one |
| # or more contributor license agreements. See the NOTICE file |
| # distributed with this work for additional information |
| # regarding copyright ownership. The ASF licenses this file |
| # to you 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 marvin.cloudstackConnection import CSConnection |
| from marvin.asyncJobMgr import asyncJobMgr |
| from marvin.dbConnection import DbConnection |
| from marvin.cloudstackAPI import * |
| from marvin.codes import (FAILED, PASS, ADMIN, DOMAIN_ADMIN, |
| USER, SUCCESS, XEN_SERVER) |
| from marvin.configGenerator import ConfigManager |
| from marvin.cloudstackException import GetDetailExceptionInfo |
| from marvin.lib.utils import (random_gen, validateList) |
| from marvin.cloudstackAPI.cloudstackAPIClient import CloudStackAPIClient |
| import copy |
| |
| class CSTestClient(object): |
| |
| ''' |
| @Desc : CloudStackTestClient is encapsulated entity for creating and |
| getting various clients viz., apiclient, |
| user api client, dbconnection, test Data parsed |
| information etc |
| @Input : |
| mgmt_details : Management Server Details |
| dbsvr_details: Database Server details of Management \ |
| Server. Retrieved from configuration file. |
| async_timeout : Timeout for Async queries |
| default_worker_threads : Number of worker threads |
| logger : provides logging facilities for this library |
| zone : The zone on which test suites using this test client will run |
| ''' |
| |
| def __init__(self, mgmt_details, |
| dbsvr_details, |
| async_timeout=3600, |
| logger=None, |
| test_data_filepath=None, |
| zone=None, |
| hypervisor_type=None): |
| self.__mgmtDetails = mgmt_details |
| self.__dbSvrDetails = dbsvr_details |
| self.__csConnection = None |
| self.__dbConnection = None |
| self.__testClient = None |
| self.__asyncTimeOut = async_timeout |
| self.__logger = logger |
| self.__apiClient = None |
| self.__userApiClient = None |
| self.__asyncJobMgr = None |
| self.__id = None |
| self.__hypervisor = hypervisor_type |
| self.__testDataFilePath = test_data_filepath |
| self.__parsedTestDataConfig = None |
| self.__zone = zone |
| self.__setHypervisorInfo() |
| |
| @property |
| def identifier(self): |
| return self.__id |
| |
| @identifier.setter |
| def identifier(self, id): |
| self.__id = id |
| |
| def getParsedTestDataConfig(self): |
| ''' |
| @Name : getParsedTestDataConfig |
| @Desc : Provides the TestData Config needed for |
| Tests are to Run |
| @Output : Returns the Parsed Test Data Dictionary |
| ''' |
| return copy.deepcopy(self.__parsedTestDataConfig) |
| |
| def getZoneForTests(self): |
| ''' |
| @Name : getZoneForTests |
| @Desc : Provides the Zone against which Tests are to run |
| If zone name provided to marvin plugin is none |
| it will get it from Test Data Config File |
| Even, if it is not available, return None |
| @Output : Returns the Zone Name |
| ''' |
| return self.__zone |
| |
| def getHypervisorInfo(self): |
| ''' |
| @Name : getHypervisorInfo |
| @Desc : Provides the hypervisor Information to test users |
| @Output : Return Hypervisor Information |
| ''' |
| return self.__hypervisor |
| |
| def __setHypervisorInfo(self): |
| ''' |
| @Name : __setHypervisorInfo |
| @Desc: Set the HyperVisor details; |
| default to XenServer |
| ''' |
| try: |
| if not self.__hypervisor: |
| self.__hypervisor = XEN_SERVER |
| return SUCCESS |
| except Exception as e: |
| print "\n Exception Occurred Under __setHypervisorInfo " \ |
| "%s" % GetDetailExceptionInfo(e) |
| return FAILED |
| |
| def __createApiClient(self): |
| try: |
| ''' |
| Step1 : Create a CS Connection Object |
| ''' |
| mgmt_details = self.__mgmtDetails |
| self.__csConnection = CSConnection(mgmt_details, |
| self.__asyncTimeOut, |
| self.__logger) |
| |
| ''' |
| Step2 : Create API Client with earlier created connection object |
| ''' |
| self.__apiClient = CloudStackAPIClient(self.__csConnection) |
| |
| ''' |
| Step3: If API Key is not provided as part of Management Details, |
| then verify and register |
| ''' |
| if mgmt_details.apiKey is None: |
| list_user = listUsers.listUsersCmd() |
| list_user.account = "admin" |
| list_user_res = self.__apiClient.listUsers(list_user) |
| if list_user_res is None or\ |
| (validateList(list_user_res)[0] != PASS): |
| self.__logger.error("__createApiClient: API " |
| "Client Creation Failed") |
| return FAILED |
| user_id = list_user_res[0].id |
| api_key = list_user_res[0].apikey |
| security_key = list_user_res[0].secretkey |
| if api_key is None: |
| ret = self.__getKeys(user_id) |
| if ret != FAILED: |
| mgmt_details.apiKey = ret[0] |
| mgmt_details.securityKey = ret[1] |
| else: |
| self.__logger.error("__createApiClient: API Client " |
| "Creation Failed while " |
| "Registering User") |
| return FAILED |
| else: |
| mgmt_details.port = 8080 |
| mgmt_details.apiKey = api_key |
| mgmt_details.securityKey = security_key |
| ''' |
| Now Create the Connection objects and Api Client using |
| new details |
| ''' |
| self.__csConnection = CSConnection(mgmt_details, |
| self.__asyncTimeOut, |
| self.__logger) |
| self.__apiClient = CloudStackAPIClient(self.__csConnection) |
| return SUCCESS |
| except Exception as e: |
| self.__logger.exception(" Exception Occurred Under " |
| "__createApiClient: %s" % |
| GetDetailExceptionInfo(e)) |
| return FAILED |
| |
| def __createDbConnection(self): |
| ''' |
| @Name : ___createDbConnection |
| @Desc : Creates the CloudStack DB Connection |
| ''' |
| host = "localhost" if self.__dbSvrDetails.dbSvr is None \ |
| else self.__dbSvrDetails.dbSvr |
| port = 3306 if self.__dbSvrDetails.port is None \ |
| else self.__dbSvrDetails.port |
| user = "cloud" if self.__dbSvrDetails.user is None \ |
| else self.__dbSvrDetails.user |
| passwd = 'cloud' if self.__dbSvrDetails.passwd is None \ |
| else self.__dbSvrDetails.passwd |
| db = 'cloud' if self.__dbSvrDetails.db is None \ |
| else self.__dbSvrDetails.db |
| self.__dbConnection = DbConnection(host, port, user, passwd, db) |
| |
| def __getKeys(self, userid): |
| ''' |
| @Name : ___getKeys |
| @Desc : Retrieves the API and Secret Key for the provided Userid |
| @Input: userid: Userid to register |
| @Output: FAILED or tuple with apikey and secretkey |
| ''' |
| try: |
| register_user = registerUserKeys.registerUserKeysCmd() |
| register_user.id = userid |
| register_user_res = \ |
| self.__apiClient.registerUserKeys(register_user) |
| if not register_user_res: |
| return FAILED |
| return (register_user_res.apikey, register_user_res.secretkey) |
| except Exception as e: |
| self.__logger.exception("Exception Occurred Under __geKeys : " |
| "%s" % GetDetailExceptionInfo(e)) |
| return FAILED |
| |
| def createTestClient(self): |
| ''' |
| @Name : createTestClient |
| @Desc : Creates the Test Client. |
| The test Client is used by test suites |
| Here we create ParsedTestData Config. |
| Creates a DB Connection. |
| Creates an API Client |
| @Output : FAILED In case of an issue\Failure |
| SUCCESS in case of Success of this function |
| ''' |
| try: |
| ''' |
| 1. Create Config Object |
| Provides the Configuration Object to test suites through |
| getConfigParser. The purpose of this config object is to |
| parse the default config and provide dictionary of the |
| config so users can use that configuration. |
| Users can later call getConfig on this object and it will |
| return the default parsed config dictionary from default |
| configuration file. They can overwrite it with |
| providing their own configuration file as well. |
| ''' |
| ''' |
| 1. Check Config,Zone,Hypervisor Information |
| ''' |
| self.__configObj = ConfigManager(self.__testDataFilePath) |
| |
| if not self.__configObj or not self.__hypervisor: |
| self.__logger.error("createTestClient : " |
| "Either Hypervisor is None or " |
| "Not able to create " |
| "ConfigManager Object") |
| return FAILED |
| |
| self.__parsedTestDataConfig = self.__configObj.getConfig() |
| self.__logger.debug("Parsing Test data successful") |
| |
| ''' |
| 2. Create DB Connection |
| ''' |
| self.__createDbConnection() |
| ''' |
| 3. Creates API Client |
| ''' |
| ret = self.__createApiClient() |
| if ret == FAILED: |
| self.__logger.\ |
| error("==== Test Client Creation Failed ====") |
| else: |
| self.__logger.\ |
| debug("==== Test Client Creation Successful ====") |
| return ret |
| except Exception as e: |
| self.__logger.exception("Exception Occurred " |
| "Under createTestClient " |
| ": %s" % GetDetailExceptionInfo(e)) |
| return FAILED |
| |
| def isAdminContext(self): |
| """ |
| @Name : isAdminContext |
| @Desc:A user is a regular user if he fails to listDomains; |
| if he is a domain-admin, he can list only domains that are non-ROOT; |
| if he is an admin, he can list the ROOT domain successfully |
| """ |
| try: |
| listdom = listDomains.listDomainsCmd() |
| listdom.name = 'ROOT' |
| listdomres = self.__apiClient.listDomains(listdom) |
| if listdomres != FAILED: |
| rootdom = listdomres[0].name |
| if rootdom == 'ROOT': |
| return ADMIN |
| else: |
| return DOMAIN_ADMIN |
| return USER |
| except: |
| return USER |
| |
| def __createUserApiClient(self, UserName, DomainName, acctType=0): |
| ''' |
| @Name : ___createUserApiClient |
| @Desc : Creates a User API Client with given |
| UserName\DomainName Parameters |
| @Input: UserName: Username to be created in cloudstack |
| DomainName: Domain under which the above account be created |
| accType: Type of Account EX: Root,Non Root etc |
| @Output: Return the API client for the user |
| ''' |
| try: |
| if not self.isAdminContext(): |
| return self.__apiClient |
| mgmt_details = self.__mgmtDetails |
| listDomain = listDomains.listDomainsCmd() |
| listDomain.listall = True |
| listDomain.name = DomainName |
| try: |
| domains = self.__apiClient.listDomains(listDomain) |
| domId = domains[0].id |
| except: |
| cdomain = createDomain.createDomainCmd() |
| cdomain.name = DomainName |
| domain = self.__apiClient.createDomain(cdomain) |
| domId = domain.id |
| |
| cmd = listAccounts.listAccountsCmd() |
| cmd.name = UserName |
| cmd.domainid = domId |
| cmd.listall = True |
| try: |
| accounts = self.__apiClient.listAccounts(cmd) |
| acctId = accounts[0].id |
| except: |
| createAcctCmd = createAccount.createAccountCmd() |
| createAcctCmd.accounttype = acctType |
| createAcctCmd.domainid = domId |
| createAcctCmd.email = "test-" + random_gen()\ |
| + "@cloudstack.org" |
| createAcctCmd.firstname = UserName |
| createAcctCmd.lastname = UserName |
| createAcctCmd.password = 'password' |
| createAcctCmd.username = UserName |
| acct = self.__apiClient.createAccount(createAcctCmd) |
| acctId = acct.id |
| |
| listuser = listUsers.listUsersCmd() |
| listuser.username = UserName |
| listuser.domainid = domId |
| listuser.listall = True |
| |
| listuserRes = self.__apiClient.listUsers(listuser) |
| userId = listuserRes[0].id |
| apiKey = listuserRes[0].apikey |
| securityKey = listuserRes[0].secretkey |
| |
| if apiKey is None: |
| ret = self.__getKeys(userId) |
| if ret != FAILED: |
| mgmt_details.apiKey = ret[0] |
| mgmt_details.securityKey = ret[1] |
| else: |
| self.__logger.error("__createUserApiClient: " |
| "User API Client Creation." |
| " While Registering User Failed") |
| return FAILED |
| else: |
| mgmt_details.port = 8080 |
| mgmt_details.apiKey = apiKey |
| mgmt_details.securityKey = securityKey |
| |
| newUserConnection =\ |
| CSConnection(mgmt_details, |
| self.__csConnection.asyncTimeout, |
| self.__csConnection.logger) |
| self.__userApiClient = CloudStackAPIClient(newUserConnection) |
| self.__userApiClient.connection = newUserConnection |
| self.__userApiClient.hypervisor = self.__hypervisor |
| return self.__userApiClient |
| except Exception as e: |
| self.__logger.exception("Exception Occurred " |
| "Under getUserApiClient : %s" % |
| GetDetailExceptionInfo(e)) |
| return FAILED |
| |
| def close(self): |
| if self.__csConnection is not None: |
| self.__csConnection.close() |
| |
| def getDbConnection(self): |
| ''' |
| @Name : getDbConnection |
| @Desc : Retrieves the DB Connection Handle |
| ''' |
| return self.__dbConnection |
| |
| def getConfigParser(self): |
| ''' |
| @Name : getConfigParser |
| @Desc : Provides the ConfigManager Interface to TestClients |
| ''' |
| return self.__configObj |
| |
| def getApiClient(self): |
| if self.__apiClient: |
| self.__apiClient.id = self.identifier |
| return self.__apiClient |
| return None |
| |
| def getUserApiClient(self, UserName=None, DomainName=None, type=0): |
| """ |
| @Name : getUserApiClient |
| @Desc : Provides the User API Client to test Users |
| 0 - user ; 1 - admin;2 - domain admin |
| @OutPut : FAILED In case of an issue |
| else User API Client |
| """ |
| if UserName is None or DomainName is None: |
| return FAILED |
| return self.__createUserApiClient(UserName, DomainName, type) |
| |
| def submitCmdsAndWait(self, cmds, workers=1, apiclient=None): |
| ''' |
| @Desc : FixME, httplib has issue if more than one thread submitted |
| ''' |
| if not apiclient: |
| apiclient = self.__apiClient |
| if self.__asyncJobMgr is None: |
| self.__asyncJobMgr = asyncJobMgr(apiclient, |
| self.__dbConnection) |
| return self.__asyncJobMgr.submitCmdsAndWait(cmds, workers) |
| |
| def submitJob(self, job, ntimes=1, nums_threads=10, interval=1): |
| ''' |
| @Desc : submit one job and execute the same job |
| ntimes, with nums_threads of threads |
| ''' |
| if self.__asyncJobMgr is None: |
| self.__asyncJobMgr = asyncJobMgr(self.__apiClient, |
| self.__dbConnection) |
| self.__asyncJobMgr.submitJobExecuteNtimes(job, ntimes, |
| nums_threads, |
| interval) |
| |
| def submitJobs(self, jobs, nums_threads=10, interval=1): |
| ''' |
| @Desc :submit n jobs, execute them with nums_threads |
| of threads |
| ''' |
| if self.__asyncJobMgr is None: |
| self.__asyncJobMgr = asyncJobMgr(self.__apiClient, |
| self.__dbConnection) |
| self.__asyncJobMgr.submitJobs(jobs, nums_threads, interval) |