From 4e9e7937ed5e56cbd7f678b2ed9863a56af16635 Mon Sep 17 00:00:00 2001 From: Prasanna Santhanam Date: Wed, 24 Apr 2013 00:06:44 +0530 Subject: [PATCH] Fix Vijay's test to use the marvin integratin libararies Changes to marvin_request and base libraries to accomadate POST requests. Additional tests for sending userdata through GET Userdata tests - send size>2k data via GET and POST. Signed-off-by: Prasanna Santhanam --- .../component/test_deploy_vm_with_userdata.py | 108 ------------- .../smoke/test_deploy_vm_with_userdata.py | 144 ++++++++++++++++++ tools/marvin/marvin/cloudstackConnection.py | 34 ++--- tools/marvin/marvin/codegenerator.py | 4 +- tools/marvin/marvin/integration/lib/base.py | 11 +- 5 files changed, 165 insertions(+), 136 deletions(-) delete mode 100644 test/integration/component/test_deploy_vm_with_userdata.py create mode 100644 test/integration/smoke/test_deploy_vm_with_userdata.py diff --git a/test/integration/component/test_deploy_vm_with_userdata.py b/test/integration/component/test_deploy_vm_with_userdata.py deleted file mode 100644 index 6b5767b6e1e..00000000000 --- a/test/integration/component/test_deploy_vm_with_userdata.py +++ /dev/null @@ -1,108 +0,0 @@ -#!/usr/bin/env python - -import marvin -from marvin import cloudstackTestCase -from marvin.cloudstackTestCase import * -from marvin.integration.lib.base import * - -import unittest -import hashlib -import random -import os -import string - -class TestDeployVmWithUserData(cloudstackTestCase): - """ - This test deploys a virtual machine into a user account - using the small service offering and builtin template - """ - def setUp(self): - password = "password" - - self.apiClient = self.testClient.getApiClient() #Get ourselves an API client - - self.acct = createAccount.createAccountCmd() #The createAccount command - self.acct.accounttype = 0 #We need a regular user. admins have accounttype=1 - self.acct.firstname = 'firstname' - self.acct.lastname = 'lastname' - self.acct.password = password - self.acct.username = 'user1' - self.acct.email = 'user1@user.com' - self.acct.account = 'user1' - self.acct.domainid = 1 #The default ROOT domain - self.acctResponse = self.apiClient.createAccount(self.acct) - - self.debug("Successfully created account: %s, user: %s, id: \ - %s"%(self.acctResponse.account.account, \ - self.acctResponse.account.username, \ - self.acctResponse.account.id)) - - # Generate userdata of 2500 bytes. This is larger than the 2048 bytes limit. - # CS however allows for upto 4K bytes in the code. So this must succeed. - # Overall, the query length must not exceed 4K, for then the json decoder - # will fail this operation at the marvin client side itself. - user_data = ''.join(random.choice(string.ascii_uppercase + string.digits) for x in range(2500)) - - self.virtual_machine = { - "displayname": "Test VM", - "username": "root", - "password": "password", - "ssh_port": 22, - "hypervisor": 'VMware', - "privateport": 22, - "publicport": 22, - "protocol": 'TCP', - } - #self.virtual_machine["userdata"] = base64.b64encode(user_data) - self.virtual_machine["userdata"] = user_data - - def test_DeployVm(self): - """ - Let's start by defining the attributes of our VM that we will be - deploying on CloudStack. We will be assuming a single zone is available - and is configured and all templates are Ready - """ - deployVmCmd = deployVirtualMachine.deployVirtualMachineCmd() - deployVmCmd.zoneid = 1 - deployVmCmd.account = self.acct.account - deployVmCmd.domainid = self.acct.domainid - deployVmCmd.templateid = 7 - deployVmCmd.serviceofferingid = 1 - - # Userdata is passed in the virtual_machine dictionary. - deployVmResponse = VirtualMachine.create( - self.apiClient, - self.virtual_machine, - accountid=self.acct.account, - domainid=self.acct.domainid, - serviceofferingid=deployVmCmd.serviceofferingid, - templateid=deployVmCmd.templateid, - zoneid=deployVmCmd.zoneid - ) - - # At this point our VM is expected to be Running. Let's find out what - # listVirtualMachines tells us about VMs in this account - - listVmCmd = listVirtualMachines.listVirtualMachinesCmd() - listVmCmd.id = deployVmResponse.id - listVmResponse = self.apiClient.listVirtualMachines(listVmCmd) - - self.assertNotEqual(len(listVmResponse), 0, "Check if the list API \ - returns a non-empty response") - - vm = listVmResponse[0] - - self.assertEqual(vm.id, deployVmResponse.id, "Check if the VM returned \ - is the same as the one we deployed") - - - self.assertEqual(vm.state, "Running", "Check if VM has reached \ - a state of running") - - def tearDown(self): - """ - Delete the account created. This will clear the VM belonging to that account as well. - """ - deleteAcct = deleteAccount.deleteAccountCmd() - deleteAcct.id = self.acctResponse.account.id - self.apiClient.deleteAccount(deleteAcct) diff --git a/test/integration/smoke/test_deploy_vm_with_userdata.py b/test/integration/smoke/test_deploy_vm_with_userdata.py new file mode 100644 index 00000000000..fd9e320addc --- /dev/null +++ b/test/integration/smoke/test_deploy_vm_with_userdata.py @@ -0,0 +1,144 @@ +# 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.cloudstackTestCase import cloudstackTestCase +from marvin.integration.lib.base import * +from marvin.integration.lib.common import get_template, get_zone, list_virtual_machines, cleanup_resources +from nose.plugins.attrib import attr + +import random +import string + +class Services: + def __init__(self): + self.services = { + "account": { + "email": "test@test.com", + "firstname": "Test", + "lastname": "User", + "username": "test", + "password": "password", + }, + "virtual_machine": { + "displayname": "Test VM", + "username": "root", + "password": "password", + "ssh_port": 22, + "hypervisor": 'XenServer', + "privateport": 22, + "publicport": 22, + "protocol": 'TCP', + }, + "ostype": 'CentOS 5.3 (64-bit)', + "service_offering": { + "name": "Tiny Instance", + "displaytext": "Tiny Instance", + "cpunumber": 1, + "cpuspeed": 100, + "memory": 256, + }, + } + + +class TestDeployVmWithUserData(cloudstackTestCase): + """Tests for UserData + """ + + @classmethod + def setUpClass(cls): + cls.apiClient = super(TestDeployVmWithUserData, cls).getClsTestClient().getApiClient() + cls.services = Services().services + cls.zone = get_zone(cls.apiClient, cls.services) + if cls.zone.localstorageenabled: + #For devcloud since localstroage is enabled + cls.services["service_offering"]["storagetype"] = "local" + cls.service_offering = ServiceOffering.create( + cls.apiClient, + cls.services["service_offering"] + ) + cls.account = Account.create(cls.apiClient, services=cls.services["account"]) + cls.template = get_template( + cls.apiClient, + cls.zone.id, + cls.services["ostype"] + ) + cls.debug("Successfully created account: %s, id: \ + %s" % (cls.account.name,\ + cls.account.id)) + cls.cleanup = [cls.account] + + # Generate userdata of 2500 bytes. This is larger than the 2048 bytes limit. + # CS however allows for upto 4K bytes in the code. So this must succeed. + # Overall, the query length must not exceed 4K, for then the json decoder + # will fail this operation at the marvin client side itcls. + user_data = ''.join(random.choice(string.ascii_uppercase + string.digits) for x in range(2500)) + cls.services["virtual_machine"]["userdata"] = user_data + + @attr(tags=["simulator", "devcloud", "basic", "advanced"]) + def test_deployvm_userdata_post(self): + """Test userdata as POST, size > 2k + """ + deployVmResponse = VirtualMachine.create( + self.apiClient, + services=self.services["virtual_machine"], + accountid=self.account.name, + domainid=self.account.domainid, + serviceofferingid=self.service_offering.id, + templateid=self.template.id, + zoneid=self.zone.id, + method='POST' + ) + vms = list_virtual_machines( + self.apiClient, + account=self.account.name, + domainid=self.account.domainid + ) + self.assert_(len(vms) > 0, "There are no Vms deployed in the account %s" % self.account.name) + vm = vms[0] + self.assert_(vm.id == str(deployVmResponse.id), "Vm deployed is different from the test") + self.assert_(vm.state == "Running", "VM is not in Running state") + + @attr(tags=["simulator", "devcloud", "basic", "advanced"]) + def test_deployvm_userdata(self): + """Test userdata as GET, size > 2k + """ + deployVmResponse = VirtualMachine.create( + self.apiClient, + services=self.services["virtual_machine"], + accountid=self.account.name, + domainid=self.account.domainid, + serviceofferingid=self.service_offering.id, + templateid=self.template.id, + zoneid=self.zone.id + ) + vms = list_virtual_machines( + self.apiClient, + account=self.account.name, + domainid=self.account.domainid + ) + self.assert_(len(vms) > 0, "There are no Vms deployed in the account %s" % self.account.name) + vm = vms[0] + self.assert_(vm.id == str(deployVmResponse.id), "Vm deployed is different from the test") + self.assert_(vm.state == "Running", "VM is not in Running state") + + @classmethod + def tearDownClass(cls): + try: + #Cleanup resources used + cleanup_resources(cls.apiClient, cls.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) diff --git a/tools/marvin/marvin/cloudstackConnection.py b/tools/marvin/marvin/cloudstackConnection.py index 214a8783263..5d30803d930 100644 --- a/tools/marvin/marvin/cloudstackConnection.py +++ b/tools/marvin/marvin/cloudstackConnection.py @@ -110,16 +110,15 @@ class cloudConnection(object): self.logging.info("Computed Signature by Marvin: %s" % signature) return signature - def request(self, command, auth=True, payload={}, data={}): + def request(self, command, auth=True, payload={}, method='GET'): """ Makes requests using auth or over integration port @param command: cloudstack API command name eg: deployVirtualMachineCommand @param auth: Authentication (apikey,secretKey) => True else False for integration.api.port - @param payload: GET param data composed as a dictionary - of key,value pairs - @param data: POST data as a dictionary + @param payload: request data composed as a dictionary + @param method: GET/POST via HTTP @return: """ payload["command"] = command @@ -131,9 +130,8 @@ class cloudConnection(object): payload["signature"] = signature try: - if data: - response = requests.get(self.baseurl, params=payload, - data=data) + if method == 'POST': + response = requests.post(self.baseurl, params=payload) else: response = requests.get(self.baseurl, params=payload) except ConnectionError, c: @@ -161,7 +159,7 @@ class cloudConnection(object): requests = {} required = [] for attribute in dir(cmd): - if attribute != "__doc__" and attribute != "__init__" and \ + if attribute != "__doc__" and attribute != "__init__" and\ attribute != "__module__": if attribute == "isAsync": isAsync = getattr(cmd, attribute) @@ -193,26 +191,20 @@ class cloudConnection(object): i = i + 1 return cmdname, isAsync, requests - def marvin_request(self, cmd, data={}, response_type=None): + def marvin_request(self, cmd, response_type=None, method='GET'): """ Requester for marvin command objects @param cmd: marvin's command from cloudstackAPI - @param data: any data to be sent in as POST @param response_type: response type of the command in cmd - @param raw: + @param method: HTTP GET/POST, defaults to GET @return: """ cmdname, isAsync, payload = self.sanitize_command(cmd) - self.logging.info("sending command: %s %s" % (cmdname, str(payload))) - if self.auth: - response = self.request( - cmdname, auth=True, payload=payload, data=data) - else: - response = self.request( - cmdname, auth=False, payload=payload, data=data) - - self.logging.info("Request: %s Response: %s" % - (response.url, response.text)) + self.logging.debug("sending %s request: %s %s" % (method, cmdname, str(payload))) + response = self.request( + cmdname, self.auth, payload=payload, method=method) + self.logging.debug("Request: %s Response: %s" % + (response.url, response.text)) response = jsonHelper.getResultObj(response.json(), response_type) if isAsync == "false": diff --git a/tools/marvin/marvin/codegenerator.py b/tools/marvin/marvin/codegenerator.py index b3b2a863029..36ba1800081 100644 --- a/tools/marvin/marvin/codegenerator.py +++ b/tools/marvin/marvin/codegenerator.py @@ -184,9 +184,9 @@ class codeGenerator: body += "\n" for cmdName in self.cmdsName: - body += self.space + 'def %s(self, command, postdata={}):\n'%cmdName + body += self.space + 'def %s(self, command, method="GET"):\n'%cmdName body += self.space + self.space + 'response = %sResponse()\n'%cmdName - body += self.space + self.space + 'response = self.connection.marvin_request(command, data=postdata, response_type=response)\n' + body += self.space + self.space + 'response = self.connection.marvin_request(command, response_type=response, method=method)\n' body += self.space + self.space + 'return response\n' body += '\n' diff --git a/tools/marvin/marvin/integration/lib/base.py b/tools/marvin/marvin/integration/lib/base.py index 92cdf81fa91..6c285233a82 100755 --- a/tools/marvin/marvin/integration/lib/base.py +++ b/tools/marvin/marvin/integration/lib/base.py @@ -102,7 +102,7 @@ class Account: def delete(self, apiclient): """Delete an account""" cmd = deleteAccount.deleteAccountCmd() - cmd.id = self.account.id + cmd.id = self.id apiclient.deleteAccount(cmd) @classmethod @@ -220,7 +220,7 @@ class VirtualMachine: def create(cls, apiclient, services, templateid=None, accountid=None, domainid=None, zoneid=None, networkids=None, serviceofferingid=None, securitygroupids=None, projectid=None, startvm=None, - diskofferingid=None, affinitygroupnames=None, hostid=None, mode='basic'): + diskofferingid=None, affinitygroupnames=None, hostid=None, mode='basic', method='GET'): """Create the instance""" cmd = deployVirtualMachine.deployVirtualMachineCmd() @@ -262,8 +262,6 @@ class VirtualMachine: if securitygroupids: cmd.securitygroupids = [str(sg_id) for sg_id in securitygroupids] - if "userdata" in services: - cmd.userdata = base64.b64encode(services["userdata"]) if "affinitygroupnames" in services: cmd.affinitygroupnames = services["affinitygroupnames"] @@ -279,7 +277,10 @@ class VirtualMachine: if hostid: cmd.hostid = hostid - virtual_machine = apiclient.deployVirtualMachine(cmd) + if "userdata" in services: + cmd.userdata = base64.b64encode(services["userdata"]) + + virtual_machine = apiclient.deployVirtualMachine(cmd, method=method) # VM should be in Running state after deploy timeout = 10