Skip to content

Commit a1b979d

Browse files
Santhosh EdukullaPrasanna Santhanam
authored andcommitted
CLOUDSTACK-4832. Added support for https to marvin.
advanced.cfg: Contains three additional flags "useHttps,certCAPath,certPath" for https usage in marvin for establishing cs connection. We will use the configuraiton under advanced.cfg provided by user to establish connection over https. If establishing the connection over https failed, then the default certs will be used. or else raise the exception, the existing http will work as it is when useHttps flag set to "False" Signed-off-by: Santhosh Edukulla <Santhosh.Edukulla@citrix.com> Signed-off-by: Prasanna Santhanam <tsp@apache.org>
1 parent 54e365d commit a1b979d

5 files changed

Lines changed: 84 additions & 53 deletions

File tree

setup/dev/advanced.cfg

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
# KIND, either express or implied. See the License for the
1515
# specific language governing permissions and limitations
1616
# under the License.
17-
1817
{
1918
"zones": [
2019
{
@@ -220,7 +219,10 @@
220219
"passwd": "password",
221220
"user": "root",
222221
"port": 8096,
223-
"hypervisor" : "simulator"
222+
"hypervisor": "simulator",
223+
"useHttps": "True",
224+
"certCAPath": "NA",
225+
"certPath": "NA"
224226
}
225227
]
226228
}

tools/marvin/marvin/cloudstackConnection.py

Lines changed: 57 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -35,29 +35,27 @@ class cloudConnection(object):
3535

3636
""" Connections to make API calls to the cloudstack management server
3737
"""
38-
39-
def __init__(self, mgtSvr, port=8096, user=None, passwd=None,
40-
apiKey=None, securityKey=None,
41-
asyncTimeout=3600, logging=None, scheme='http',
42-
path='client/api'):
38+
def __init__(self, mgmtDet,asyncTimeout=3600, logging=None, scheme='http',path='client/api'):
4339
self.loglevel() # Turn off requests logs
44-
self.apiKey = apiKey
45-
self.securityKey = securityKey
46-
self.mgtSvr = mgtSvr
47-
self.port = port
48-
self.user = user
49-
self.passwd = passwd
40+
self.apiKey = mgmtDet.apiKey
41+
self.securityKey = mgmtDet.securityKey
42+
self.mgtSvr = mgmtDet.mgtSvrIp
43+
self.port = mgmtDet.port
44+
self.user = mgmtDet.user
45+
self.passwd = mgmtDet.passwd
46+
self.certCAPath = mgmtDet.certCAPath
47+
self.certPath = mgmtDet.certPath
5048
self.logging = logging
5149
self.path = path
5250
self.retries = 5
51+
self.protocol = "http"
5352
self.asyncTimeout = asyncTimeout
5453
self.auth = True
55-
if port == 8096 or \
54+
if self.port == 8096 or \
5655
(self.apiKey is None and self.securityKey is None):
5756
self.auth = False
58-
if scheme not in ['http', 'https']:
59-
raise RequestException("Protocol must be HTTP")
60-
self.protocol = scheme
57+
if mgmtDet.useHttps == "True":
58+
self.protocol = "https"
6159
self.baseurl = "%s://%s:%d/%s"\
6260
% (self.protocol, self.mgtSvr, self.port, self.path)
6361

@@ -145,15 +143,52 @@ def request(self, command, auth=True, payload={}, method='GET'):
145143
payload["signature"] = signature
146144

147145
try:
148-
if method == 'POST':
149-
response = requests.post(
150-
self.baseurl, params=payload, verify=False)
146+
'''
147+
https_flag : Signifies whether to verify connection over http or https, if set to true uses https otherwise http
148+
cert_path : Signifies ca and cert path required by requests library for the connection
149+
'''
150+
https_flag = False
151+
cert_path = ()
152+
if self.protocol == "https":
153+
https_flag = True
154+
if self.certCAPath != "NA" and self.certPath != "NA":
155+
cert_path = ( self.certCAPath,self.certPath )
156+
157+
'''
158+
Verify whether protocol is "http", then call the request over http
159+
'''
160+
if self.protocol == "http":
161+
if method == 'POST':
162+
response = requests.post(self.baseurl, params=payload, verify=https_flag)
163+
else:
164+
response = requests.get(self.baseurl, params=payload, verify=https_flag)
151165
else:
152-
response = requests.get(
153-
self.baseurl, params=payload, verify=False)
166+
exception_check = False
167+
exception_info = None
168+
'''
169+
If protocol is https, then request the url with user provided certificates provided as part of cert
170+
'''
171+
try:
172+
if method == 'POST':
173+
response = requests.post(self.baseurl, params=payload, cert=cert_path, verify=https_flag)
174+
else:
175+
response = requests.get(self.baseurl, params=payload, cert=cert_path, verify=https_flag)
176+
except Exception,e:
177+
'''
178+
If an exception occurs with current CA certs, then try with default certs path, we dont need to mention here the cert path
179+
'''
180+
self.logging.debug( "Creating CS connection over https didnt worked with user provided certs %s"%e )
181+
exception_check = True
182+
exception_info = e
183+
if method == 'POST':
184+
response = requests.post(self.baseurl, params=payload, verify=https_flag)
185+
else:
186+
response = requests.get(self.baseurl, params=payload, verify=https_flag)
187+
finally:
188+
if exception_check == True and exception_info is not None:
189+
raise exception_info
154190
except ConnectionError, c:
155-
self.logging.debug("Connection refused. Reason: %s : %s" %
156-
(self.baseurl, c))
191+
self.logging.debug("Connection refused. Reason: %s : %s" %(self.baseurl, c))
157192
raise c
158193
except HTTPError, h:
159194
self.logging.debug("Server returned error code: %s" % h)

tools/marvin/marvin/cloudstackTestClient.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,9 @@
2525

2626

2727
class cloudstackTestClient(object):
28-
def __init__(self, mgtSvr=None, port=8096, user=None, passwd=None,
29-
apiKey=None, securityKey=None, asyncTimeout=3600,
30-
defaultWorkerThreads=10, logging=None):
28+
def __init__(self,mgmtDetails,asyncTimeout=3600,defaultWorkerThreads=10, logging=None):
3129
self.connection = \
32-
cloudstackConnection.cloudConnection(mgtSvr, port, user, passwd,
33-
apiKey, securityKey,
34-
asyncTimeout, logging)
30+
cloudstackConnection.cloudConnection( mgmtDetails,asyncTimeout,logging)
3531
self.apiClient =\
3632
cloudstackAPIClient.CloudStackAPIClient(self.connection)
3733
self.dbConnection = None

tools/marvin/marvin/configGenerator.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ def __init__(self):
2727
self.port = 8096
2828
self.apiKey = None
2929
self.securityKey = None
30+
self.useHttps = None
31+
self.certCAPath = None
32+
self.certPath = None
3033

3134

3235
class dbServer(object):
@@ -830,15 +833,17 @@ def generate_setup_config(config, file=None):
830833
def get_setup_config(file):
831834
if not os.path.exists(file):
832835
raise IOError("config file %s not found. \
833-
please specify a valid config file" % file)
836+
please specify a valid config file" % file)
834837
config = cloudstackConfiguration()
835838
configLines = []
836839
with open(file, 'r') as fp:
837840
for line in fp:
838841
ws = line.strip()
839842
if not ws.startswith("#"):
840843
configLines.append(ws)
841-
config = json.loads("\n".join(configLines))
844+
k = json.loads("\n".join(configLines))
845+
#config = json.loads("\n".join(configLines))
846+
config = k
842847
return jsonHelper.jsonLoader(config)
843848

844849
if __name__ == "__main__":

tools/marvin/marvin/deployDataCenter.py

Lines changed: 14 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -512,11 +512,9 @@ def loadCfg(self):
512512
try:
513513
self.config = configGenerator.get_setup_config(self.configFile)
514514
except:
515-
raise cloudstackException.InvalidParameterException(
516-
"Failed to load config %s" % self.configFile)
517-
518-
mgt = self.config.mgtSvr[0]
515+
raise cloudstackException.InvalidParameterException("Failed to load config %s" % self.configFile)
519516

517+
mgtDetails = self.config.mgtSvr[0]
520518
loggers = self.config.logger
521519
testClientLogFile = None
522520
self.testCaseLogFile = None
@@ -535,38 +533,35 @@ def loadCfg(self):
535533
testClientLogger = logging.getLogger("testclient.testengine.run")
536534
fh = logging.FileHandler(testClientLogFile)
537535
fh.setFormatter(logging.
538-
Formatter("%(asctime)s - %(levelname)s - %(name)s\
539-
- %(message)s"))
536+
Formatter("%(asctime)s - %(levelname)s - %(name)s\
537+
- %(message)s"))
540538
testClientLogger.addHandler(fh)
541539
testClientLogger.setLevel(logging.INFO)
542540
self.testClientLogger = testClientLogger
543541

544542
self.testClient = \
545543
cloudstackTestClient.\
546-
cloudstackTestClient(mgt.mgtSvrIp, mgt.port, mgt.user, mgt.passwd,
547-
mgt.apiKey, mgt.securityKey,
548-
logging=self.testClientLogger)
549-
if mgt.apiKey is None:
550-
apiKey, securityKey = self.registerApiKey()
551-
self.testClient = cloudstackTestClient.cloudstackTestClient(
552-
mgt.mgtSvrIp, 8080,
553-
mgt.user, mgt.passwd,
554-
apiKey, securityKey,
555-
logging=self.testClientLogger)
544+
cloudstackTestClient( mgtDetails,logging=self.testClientLogger)
545+
546+
if mgtDetails.apiKey is None:
547+
mgtDetails.apiKey,mgtDetails.securityKey = self.registerApiKey()
548+
mgtDetails.port = 8080
549+
self.testClient = cloudstackTestClient.cloudstackTestClient( mgtDetails,logging=self.testClientLogger)
556550

557551
"""config database"""
558552
dbSvr = self.config.dbSvr
559553
if dbSvr is not None:
560-
self.testClient.dbConfigure(dbSvr.dbSvr, dbSvr.port, dbSvr.user,
554+
self.testClient.dbConfigure(dbSvr.dbSvr, dbSvr.port, dbSvr.user, \
561555
dbSvr.passwd, dbSvr.db)
562556

563557
self.apiClient = self.testClient.getApiClient()
564558
"""set hypervisor"""
565-
if mgt.hypervisor:
566-
self.apiClient.hypervisor = mgt.hypervisor
559+
if mgtDetails.hypervisor:
560+
self.apiClient.hypervisor = mgtDetails.hypervisor
567561
else:
568562
self.apiClient.hypervisor = "XenServer" # Defaults to Xenserver
569563

564+
570565
def updateConfiguration(self, globalCfg):
571566
if globalCfg is None:
572567
return None
@@ -578,15 +573,13 @@ def updateConfiguration(self, globalCfg):
578573
self.apiClient.updateConfiguration(updateCfg)
579574

580575
def copyAttributesToCommand(self, source, command):
581-
582576
map(lambda attr: setattr(command, attr, getattr(source, attr, None)),
583577
filter(lambda attr: not attr.startswith("__") and attr not in
584578
["required", "isAsync"], dir(command)))
585579

586580
def configureS3(self, s3):
587581
if s3 is None:
588582
return
589-
590583
command = addS3.addS3Cmd()
591584
self.copyAttributesToCommand(s3, command)
592585
self.apiClient.addS3(command)

0 commit comments

Comments
 (0)