mirror of https://github.com/apache/cloudstack.git
427 lines
17 KiB
Python
427 lines
17 KiB
Python
# 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 cloudstackConnection import CSConnection
|
|
import asyncJobMgr
|
|
from dbConnection import DbConnection
|
|
from cloudstackAPI import *
|
|
import random
|
|
import string
|
|
import hashlib
|
|
from codes import (FAILED, PASS, ADMIN, DOMAIN_ADMIN,
|
|
USER, SUCCESS, XEN_SERVER)
|
|
from configGenerator import ConfigManager
|
|
from marvin.lib import utils
|
|
from marvin.cloudstackException import GetDetailExceptionInfo
|
|
from marvin.lib.utils import (random_gen, validateList)
|
|
from marvin.cloudstackAPI.cloudstackAPIClient import CloudStackAPIClient
|
|
|
|
|
|
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,
|
|
default_worker_threads=10,
|
|
logger=None,
|
|
test_data_filepath=None,
|
|
zone=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.__defaultWorkerThreads = default_worker_threads
|
|
self.__apiClient = None
|
|
self.__userApiClient = None
|
|
self.__asyncJobMgr = None
|
|
self.__id = None
|
|
self.__testDataFilePath = test_data_filepath
|
|
self.__parsedTestDataConfig = None
|
|
self.__zone = zone
|
|
|
|
@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 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
|
|
'''
|
|
if self.__zone is None:
|
|
if self.__parsedTestDataConfig is not None:
|
|
self.__zone = self.__parsedTestDataConfig.get("zone")
|
|
return self.__zone
|
|
|
|
def __setHypervisorToClient(self):
|
|
'''
|
|
@Name : ___setHypervisorToClient
|
|
@Desc: Set the HyperVisor Details under API Client;
|
|
default to Xen
|
|
'''
|
|
if self.__mgmtDetails.hypervisor:
|
|
self.__apiClient.hypervisor = self.__mgmtDetails.hypervisor
|
|
else:
|
|
self.__apiClient.hypervisor = XEN_SERVER
|
|
|
|
def __createApiClient(self):
|
|
try:
|
|
'''
|
|
Step1 : Create a CS Connection Object
|
|
'''
|
|
self.__csConnection = CSConnection(self.__mgmtDetails,
|
|
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 self.__mgmtDetails.apiKey is None:
|
|
list_user = listUsers.listUsersCmd()
|
|
list_user.account = "admin"
|
|
list_user_res = self.__apiClient.listUsers(list_user)
|
|
if list_user_res == FAILED or 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:
|
|
self.__mgmtDetails.port = 8080
|
|
self.__mgmtDetails.apiKey = ret[0]
|
|
self.__mgmtDetails.securityKey = ret[1]
|
|
else:
|
|
self.__logger.error("__createApiClient: API Client "
|
|
"Creation Failed while "
|
|
"Registering User")
|
|
return FAILED
|
|
'''
|
|
Now Create the Connection objects and Api Client using
|
|
new details
|
|
'''
|
|
self.__csConnection = CSConnection(self.__mgmtDetails,
|
|
self.__asyncTimeOut,
|
|
self.__logger)
|
|
self.__apiClient = CloudStackAPIClient(self.__csConnection)
|
|
'''
|
|
Set the HyperVisor Details to Client default to Xen
|
|
'''
|
|
self.__setHypervisorToClient()
|
|
return SUCCESS
|
|
except Exception, 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.passd is None \
|
|
else self.__dbSvrDetails.passd
|
|
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
|
|
'''
|
|
try:
|
|
register_user = registerUserKeys.registerUserKeysCmd()
|
|
register_user.id = userid
|
|
register_user_res = \
|
|
self.__apiClient.registerUserKeys(register_user)
|
|
if register_user_res == FAILED:
|
|
return FAILED
|
|
return (register_user_res.apikey, register_user_res.secretkey)
|
|
except Exception, 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.
|
|
'''
|
|
self.__configObj = ConfigManager(self.__testDataFilePath)
|
|
if self.__configObj:
|
|
self.__parsedTestDataConfig = self.__configObj.getConfig()
|
|
self.__logger.debug("Parsing Test data successful")
|
|
else:
|
|
self.__logger.error("createTestClient : Not able to create "
|
|
"ConfigManager Object")
|
|
return FAILED
|
|
'''
|
|
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, 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
|
|
'''
|
|
try:
|
|
if not self.isAdminContext():
|
|
return self.__apiClient
|
|
|
|
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
|
|
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
|
|
|
|
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:
|
|
mgtDetails = self.__mgmtDetails
|
|
mgtDetails.apiKey = ret[0]
|
|
mgtDetails.securityKey = ret[1]
|
|
else:
|
|
self.__logger.error("__createUserApiClient: "
|
|
"User API Client Creation."
|
|
" While Registering User Failed")
|
|
return FAILED
|
|
|
|
newUserConnection =\
|
|
CSConnection(mgtDetails,
|
|
self.__csConnection.asyncTimeout,
|
|
self.__csConnection.logger)
|
|
self.__userApiClient = CloudStackAPIClient(newUserConnection)
|
|
self.__userApiClient.connection = newUserConnection
|
|
self.__userApiClient.hypervisor = self.__apiClient.hypervisor
|
|
return self.__userApiClient
|
|
except Exception, 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, account, domain, type=0):
|
|
"""
|
|
@Name : getUserApiClient
|
|
@Desc : Provides the User API Client to Users
|
|
0 - user ; 1 - admin;2 - domain admin
|
|
@OutPut : FAILED In case of an issue
|
|
else User API Client
|
|
"""
|
|
return FAILED if (self.__createUserApiClient(account,
|
|
domain,
|
|
type)
|
|
== FAILED) \
|
|
else self.__userApiClient
|
|
|
|
def submitCmdsAndWait(self, cmds, workers=1):
|
|
'''
|
|
@Desc : FixME, httplib has issue if more than one thread submitted
|
|
'''
|
|
if self.__asyncJobMgr is None:
|
|
self.__asyncJobMgr = asyncJobMgr.asyncJobMgr(self.__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.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.asyncJobMgr(self.__apiClient,
|
|
self.__dbConnection)
|
|
self.__asyncJobMgr.submitJobs(jobs, nums_threads, interval)
|