blob: d6564cf5ea8ed08e63d4d1c5aa15d66d8c30b05b [file] [log] [blame]
# 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.
import marvin
from sys import stdout, exit
import logging
import time
import os
import nose.core
from marvin.cloudstackTestCase import cloudstackTestCase
from marvin.marvinInit import MarvinInit
from nose.plugins.base import Plugin
from marvin.codes import (SUCCESS,
FAILED,
EXCEPTION)
from marvin.lib.utils import random_gen
from marvin.cloudstackException import GetDetailExceptionInfo
class MarvinPlugin(Plugin):
"""
Custom plugin for the cloudstackTestCases to be run using nose
"""
name = "marvin"
def __init__(self):
self.__identifier = None
self.__testClient = None
self.__logFolderPath = None
self.__parsedConfig = None
'''
Contains Config File
'''
self.__configFile = None
'''
Signifies the Zone against which all tests will be Run
'''
self.__zoneForTests = None
'''
Signifies the flag whether to deploy the New DC or Not
'''
self.__deployDcFlag = None
self.conf = None
self.__resultStream = stdout
self.__testRunner = None
self.__testResult = SUCCESS
self.__startTime = None
self.__testName = None
self.__tcRunLogger = None
self.__testModName = ''
self.__hypervisorType = None
'''
The Log Path provided by user where all logs are routed to
'''
self.__userLogPath = None
Plugin.__init__(self)
def configure(self, options, conf):
"""enable the marvin plugin when the --with-marvin directive is given
to nose. The enableOpt value is set from the command line directive and
self.enabled (True|False) determines whether marvin's tests will run.
By default non-default plugins like marvin will be disabled
"""
self.enabled = True
if hasattr(options, self.enableOpt):
if not getattr(options, self.enableOpt):
self.enabled = False
return
self.__configFile = options.configFile
self.__deployDcFlag = options.deployDc
self.__zoneForTests = options.zone
self.__hypervisorType = options.hypervisor_type
self.__userLogPath = options.logFolder
self.conf = conf
if self.startMarvin() == FAILED:
print("\nStarting Marvin Failed, exiting. Please Check")
exit(1)
def options(self, parser, env):
"""
Register command line options
"""
parser.add_option("--marvin-config", action="store",
default=env.get('MARVIN_CONFIG',
'./datacenter.cfg'),
dest="configFile",
help="Marvin's configuration file is required."
"The config file containing the datacenter and "
"other management server "
"information is specified")
parser.add_option("--deploy", action="store_true",
default=False,
dest="deployDc",
help="Deploys the DC with Given Configuration."
"Requires only when DC needs to be deployed")
parser.add_option("--zone", action="store",
default=None,
dest="zone",
help="Runs all tests against this specified zone")
parser.add_option("--hypervisor", action="store",
default=None,
dest="hypervisor_type",
help="Runs all tests against the specified "
"zone and hypervisor Type")
parser.add_option("--log-folder-path", action="store",
default=None,
dest="logFolder",
help="Collects all logs under the user specified"
"folder"
)
Plugin.options(self, parser, env)
def wantClass(self, cls):
if cls.__name__ == 'cloudstackTestCase':
return False
if issubclass(cls, cloudstackTestCase):
return True
return None
def __checkImport(self, filename):
'''
@Name : __checkImport
@Desc : Verifies to run the available test module for any Import
Errors before running and check
whether if it is importable.
This will check for test modules which has some issues to be
getting imported.
Returns False or True based upon the result.
'''
try:
if os.path.isfile(filename):
ret = os.path.splitext(filename)
if ret[1] == ".py":
os.system("python " + filename)
return True
return False
except ImportError as e:
print("FileName :%s : Error : %s" % \
(filename, GetDetailExceptionInfo(e)))
return False
def wantFile(self, filename):
'''
@Desc : Only python files will be used as test modules
'''
return self.__checkImport(filename)
def loadTestsFromTestCase(self, cls):
if cls.__name__ != 'cloudstackTestCase':
self.__identifier = cls.__name__
self._injectClients(cls)
def beforeTest(self, test):
self.__testModName = test.__str__()
self.__testName = test.__str__().split()[0]
if not self.__testName:
self.__testName = "test"
self.__testClient.identifier = '-'.\
join([self.__identifier if self.__identifier != None else "marvinTest", self.__testName])
if self.__tcRunLogger:
self.__tcRunLogger.name = test.__str__()
def startTest(self, test):
"""
Currently used to record start time for tests
Dump Start Msg of TestCase to Log
"""
if self.__tcRunLogger:
self.__tcRunLogger.debug("::::::::::::STARTED : TC: " +
str(self.__testName) + " :::::::::::")
self.__startTime = time.time()
def printMsg(self, status, tname, err):
if status in [FAILED, EXCEPTION] and self.__tcRunLogger:
self.__tcRunLogger.\
fatal("%s: %s: %s" % (status,
tname,
GetDetailExceptionInfo(err)))
write_str = "=== TestName: %s | Status : %s ===\n" % (tname, status)
self.__resultStream.write(write_str)
print(write_str)
def addSuccess(self, test, capt):
'''
Adds the Success Messages to logs
'''
self.printMsg(SUCCESS, self.__testName, "Test Case Passed")
self.__testresult = SUCCESS
def handleError(self, test, err):
'''
Adds Exception throwing test cases and information to log.
'''
self.printMsg(EXCEPTION, self.__testName, GetDetailExceptionInfo(err))
self.__testResult = EXCEPTION
def prepareTestRunner(self, runner):
if self.__testRunner:
return self.__testRunner
def handleFailure(self, test, err):
'''
Adds Failing test cases and information to log.
'''
self.printMsg(FAILED, self.__testName, GetDetailExceptionInfo(err))
self.__testResult = FAILED
def startMarvin(self):
'''
@Name : startMarvin
@Desc : Initializes the Marvin
creates the test Client
creates the runlogger for logging
Parses the config and creates a parsedconfig
Creates a debugstream for tc debug log
'''
try:
obj_marvininit = MarvinInit(self.__configFile,
self.__deployDcFlag,
None,
self.__zoneForTests,
self.__hypervisorType,
self.__userLogPath)
if obj_marvininit and obj_marvininit.init() == SUCCESS:
self.__testClient = obj_marvininit.getTestClient()
self.__tcRunLogger = obj_marvininit.getLogger()
self.__parsedConfig = obj_marvininit.getParsedConfig()
self.__resultStream = obj_marvininit.getResultFile()
self.__logFolderPath = obj_marvininit.getLogFolderPath()
self.__testRunner = nose.core.\
TextTestRunner(stream=self.__resultStream,
descriptions=True,
verbosity=2,
config=self.conf)
return SUCCESS
return FAILED
except Exception as e:
print("Exception Occurred under startMarvin: %s" % \
GetDetailExceptionInfo(e))
return FAILED
def stopTest(self, test):
"""
Currently used to record end time for tests
"""
endTime = time.time()
if self.__startTime:
totTime = int(endTime - self.__startTime)
if self.__tcRunLogger:
self.__tcRunLogger.\
debug("TestCaseName: %s; "
"Time Taken: %s Seconds; StartTime: %s; "
"EndTime: %s; Result: %s" %
(self.__testName, str(totTime),
str(time.ctime(self.__startTime)),
str(time.ctime(endTime)),
self.__testResult))
def _injectClients(self, test):
setattr(test, "debug", self.__tcRunLogger.debug)
setattr(test, "info", self.__tcRunLogger.info)
setattr(test, "warn", self.__tcRunLogger.warning)
setattr(test, "error", self.__tcRunLogger.error)
setattr(test, "testClient", self.__testClient)
setattr(test, "config", self.__parsedConfig)
if self.__testClient.identifier is None:
self.__testClient.identifier = self.__identifier
setattr(test, "clstestclient", self.__testClient)
if hasattr(test, "user"):
# when the class-level attr applied. all test runs as 'user'
self.__testClient.getUserApiClient(test.UserName,
test.DomainName,
test.AcctType)
def finalize(self, result):
try:
src = self.__logFolderPath
tmp = ''
if not self.__userLogPath:
log_cfg = self.__parsedConfig.logger
tmp = log_cfg.__dict__.get('LogFolderPath') + "/MarvinLogs"
else:
tmp = self.__userLogPath + "/MarvinLogs"
dst = tmp + "//" + random_gen()
mod_name = "test_suite"
if self.__testModName:
mod_name = self.__testModName.split(".")
if len(mod_name) > 2:
mod_name = mod_name[-2]
if mod_name and type(mod_name) is str:
dst = tmp + "/" + mod_name + "_" + random_gen()
cmd = "mv " + src + " " + dst
os.system(cmd)
print("=== Final results are now copied to: %s ===" % str(dst))
except Exception as e:
print("=== Exception occurred under finalize :%s ===" % \
str(GetDetailExceptionInfo(e)))