marvin_refactor: New firewall rule test Firewall entities were mixed up with multiple __init__'s. This is now resolved by ensuring only one creator exists per enitity. Only entities affected by this are template, sshkeypair and firewall.
Introduced a new utils module for utitlity functions. Deprecate the old utils in legacy package. Signed-off-by: Prasanna Santhanam <t...@apache.org> Project: http://git-wip-us.apache.org/repos/asf/cloudstack/repo Commit: http://git-wip-us.apache.org/repos/asf/cloudstack/commit/904a2bde Tree: http://git-wip-us.apache.org/repos/asf/cloudstack/tree/904a2bde Diff: http://git-wip-us.apache.org/repos/asf/cloudstack/diff/904a2bde Branch: refs/heads/marvin_refactor Commit: 904a2bde0a4b1eb625bc0cf8d26220d2a3de1b8b Parents: 70a9d3e Author: Prasanna Santhanam <t...@apache.org> Authored: Tue Sep 17 16:59:36 2013 +0530 Committer: Prasanna Santhanam <t...@apache.org> Committed: Thu Oct 31 13:54:25 2013 +0530 ---------------------------------------------------------------------- pom.xml | 1 + .../marvin/marvin/factory/data/firewallrule.py | 6 +- tools/marvin/marvin/factory/data/vm.py | 6 +- tools/marvin/marvin/generate/entity.py | 42 +++++++++- tools/marvin/marvin/generate/linguist.py | 2 + tools/marvin/marvin/test/test_factories.py | 57 +++++++++++++- tools/marvin/marvin/util.py | 80 ++++++++++++++++++++ 7 files changed, 183 insertions(+), 11 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cloudstack/blob/904a2bde/pom.xml ---------------------------------------------------------------------- diff --git a/pom.xml b/pom.xml index 75eae87..2880a35 100644 --- a/pom.xml +++ b/pom.xml @@ -86,6 +86,7 @@ <cs.target.dir>target</cs.target.dir> <cs.daemon.version>1.0.10</cs.daemon.version> <cs.jna.version>4.0.0</cs.jna.version> + <cs.maven.ant.version>1.7</cs.maven.ant.version> </properties> <distributionManagement> http://git-wip-us.apache.org/repos/asf/cloudstack/blob/904a2bde/tools/marvin/marvin/factory/data/firewallrule.py ---------------------------------------------------------------------- diff --git a/tools/marvin/marvin/factory/data/firewallrule.py b/tools/marvin/marvin/factory/data/firewallrule.py index da6284a..692450b 100644 --- a/tools/marvin/marvin/factory/data/firewallrule.py +++ b/tools/marvin/marvin/factory/data/firewallrule.py @@ -15,15 +15,15 @@ # specific language governing permissions and limitations # under the License. -from marvin.factory.firewallrule import FirewallRuleFactory +from marvin.factory.firewall import FirewallFactory -class SshFirewallRuleFactory(FirewallRuleFactory): +class SshFirewallRuleFactory(FirewallFactory): protocol = 'tcp' startport = 22 endport = 22 cidrlist = '0.0.0.0/0' -class HttpFirewallRuleFactory(FirewallRuleFactory): +class HttpFirewallRuleFactory(FirewallFactory): protocol = 'tcp' startport = 80 endport = 80 http://git-wip-us.apache.org/repos/asf/cloudstack/blob/904a2bde/tools/marvin/marvin/factory/data/vm.py ---------------------------------------------------------------------- diff --git a/tools/marvin/marvin/factory/data/vm.py b/tools/marvin/marvin/factory/data/vm.py index 0d7aac0..a09abe7 100644 --- a/tools/marvin/marvin/factory/data/vm.py +++ b/tools/marvin/marvin/factory/data/vm.py @@ -17,16 +17,16 @@ from marvin.factory.virtualmachine import VirtualMachineFactory - class VirtualMachineIsolatedNetwork(VirtualMachineFactory): """ - Create a virtualmachine in an isolated network typically in an advanced zone + Creates a virtualmachine in an isolated network typically in an advanced zone inside a user account - Uses a serviceoffering of tinyInstance + Uses a serviceoffering of tinyInstance of the shared storage type Uses a builtin template available Deploys in the first zone available """ + apiclient = None serviceofferingid = None templateid = None zoneid = None http://git-wip-us.apache.org/repos/asf/cloudstack/blob/904a2bde/tools/marvin/marvin/generate/entity.py ---------------------------------------------------------------------- diff --git a/tools/marvin/marvin/generate/entity.py b/tools/marvin/marvin/generate/entity.py index b9f825b..2d7b627 100644 --- a/tools/marvin/marvin/generate/entity.py +++ b/tools/marvin/marvin/generate/entity.py @@ -57,13 +57,48 @@ class Entity(object): update.body.append(self.tabspace + 'return self') return update + def creators(self, apis): + _creator_list = ['create', 'register', 'deploy'] + for c in _creator_list: + if filter(lambda a: a['apimodule'].startswith(c), apis): + return c + else: + return None + + def special_creators(self, entity): + """Entities that do not conform to the regular creator rules + """ + entity = entity.lower() + if entity == 'template': + return 'registerTemplate' + elif entity == 'iso': + return 'registerIso' + elif entity == 'firewall': + return 'createFirewallRule' + elif entity == 'ipaddress': + return 'associateIpAddress' + else: + return None + + def find_creator(self, entity, actions): + c = self.special_creators(entity) + if c: + return c + c = self.creators(actions.values()) + if c: + return c + else: + return None + def generate_entity(self, entity, actions): self.imports.append('from cloudstackentity import CloudStackEntity') self.name = entity self.classname = 'class %s(CloudStackEntity):' % entity + self.creator = self.find_creator(entity, actions) for action, details in actions.iteritems(): self.imports.append('from marvin.cloudstackAPI import %s' % details['apimodule']) m = Method(action) + m.creator = self.creator and details['apimodule'].startswith(self.creator) self.methods.append(m) #TODO: doc to explain what possible args go into **kwargs m.docstring = 'Placeholder for docstring\n' + 'optional arguments (**kwargs): [%s]"""' % ', '.join( @@ -96,7 +131,7 @@ class Entity(object): m.signature = 'def __init__(self, apiclient=None, %s, factory=None, **kwargs):' % ( ', '.join(map(lambda arg: arg + '=None', list(set(details['args']))))) else: - m.signature = 'def %s(cls, apiclient, factory=None, **kwargs):' % action + m.signature = 'def __init__(self, apiclient=None, factory=None, **kwargs):' m.body.append(self.tabspace + 'self.__update__(kwargs)') m.body.append(self.tabspace + 'if not apiclient:') @@ -126,6 +161,7 @@ class Method(object): self.action = action self.docstring = None self.signature = None + self.creator = False self.body = [] def is_creator(self): @@ -135,9 +171,7 @@ class Method(object): @param action: action verb @return: True if creator False otherwise """ - if self.action.startswith('create') \ - or self.action.startswith('register') \ - or self.action.startswith('deploy'): + if self.creator: return True return False http://git-wip-us.apache.org/repos/asf/cloudstack/blob/904a2bde/tools/marvin/marvin/generate/linguist.py ---------------------------------------------------------------------- diff --git a/tools/marvin/marvin/generate/linguist.py b/tools/marvin/marvin/generate/linguist.py index fd2729e..ec1f693 100644 --- a/tools/marvin/marvin/generate/linguist.py +++ b/tools/marvin/marvin/generate/linguist.py @@ -87,6 +87,8 @@ def entity_adjust(entity): #Cloudstack returns Register entity for registerUserKeys elif entity == 'Register': return 'UserKeys' + elif entity == 'FirewallRule': + return 'Firewall' #Cloudstack maintains Template/ISO/Volume as single Image type #elif entity in ['Template', 'Volume']: # return 'Image' http://git-wip-us.apache.org/repos/asf/cloudstack/blob/904a2bde/tools/marvin/marvin/test/test_factories.py ---------------------------------------------------------------------- diff --git a/tools/marvin/marvin/test/test_factories.py b/tools/marvin/marvin/test/test_factories.py index c2c823c..89d67fb 100644 --- a/tools/marvin/marvin/test/test_factories.py +++ b/tools/marvin/marvin/test/test_factories.py @@ -28,9 +28,12 @@ from marvin.factory.data.template import * from marvin.factory.data.user import * from marvin.factory.data.networkoffering import * from marvin.factory.data.network import * +from marvin.factory.data.vm import * +from marvin.factory.data.firewallrule import * from marvin.factory.virtualmachine import * +from marvin.entity.firewall import Firewall from marvin.entity.serviceoffering import ServiceOffering from marvin.entity.networkoffering import NetworkOffering from marvin.entity.zone import Zone @@ -40,6 +43,8 @@ from marvin.entity.user import User from marvin.entity.network import Network from marvin.entity.ipaddress import IpAddress +from marvin.util import * + class BuildVsCreateStrategyTest(unittest.TestCase): def setUp(self): @@ -228,4 +233,54 @@ class IpAddressFactoryTest(unittest.TestCase): firstip = all_ips[0] networks = Network.list(apiclient=self.apiClient, account = accnt.name, domainid = accnt.domainid) - firstip.associate(apiclient=self.apiClient, networkid = networks[0].id) \ No newline at end of file + firstip.associate(apiclient=self.apiClient, networkid = networks[0].id) + + +class FirewallRuleFactoryTest(unittest.TestCase): + def setUp(self): + self.apiClient = cloudstackTestClient(mgtSvr='localhost', + logging=logging.getLogger('factory.cloudstack')).getApiClient() + + def tearDown(self): + self.account.delete(apiclient=self.apiClient) + + @attr(tags='firewall') + def test_firewallRuleFactoryTest(self): + self.account = UserAccountFactory(apiclient=self.apiClient) + domainid = get_domain(self.apiClient).id + self.account |should| be_instance_of(Account) + vm = VirtualMachineFactory( + apiclient=self.apiClient, + account=self.account.name, + domainid=domainid, + templateid=get_template(self.apiClient).id, + serviceofferingid=get_service_offering(self.apiClient).id, + zoneid=get_zone(self.apiClient).id + ) + vm |should| be_instance_of(VirtualMachine) + vm.state |should| equal_to('Running') + vm.nic |should_not| equal_to(None) + vm.nic |should| be_instance_of(list) + + ipaddresses = IpAddress.listPublic( + apiclient=self.apiClient, + networkid=vm.nic[0].networkid + ) + ipaddresses |should_not| equal_to(None) + ipaddresses |should| be_instance_of(list) + + ipaddress = IpAddress( + apiclient=self.apiClient, + account=self.account.name, + domainid=domainid, + zoneid=get_zone(self.apiClient).id + ) + ipaddress |should_not| be(None) + ipaddress |should| be_instance_of(IpAddress) + + fwrule = SshFirewallRuleFactory( + apiclient=self.apiClient, + ipaddressid=ipaddress.ipaddress.id + ) + fwrule |should_not| be(None) + fwrule |should| be_instance_of(Firewall) http://git-wip-us.apache.org/repos/asf/cloudstack/blob/904a2bde/tools/marvin/marvin/util.py ---------------------------------------------------------------------- diff --git a/tools/marvin/marvin/util.py b/tools/marvin/marvin/util.py new file mode 100644 index 0000000..9a97c9d --- /dev/null +++ b/tools/marvin/marvin/util.py @@ -0,0 +1,80 @@ +# 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.entity.template import Template +from marvin.entity.zone import Zone +from marvin.entity.serviceoffering import ServiceOffering +from marvin.entity.domain import Domain +from marvin.entity.guestos import GuestOS + +def get_domain(apiclient): + "Returns a default `ROOT` domain" + + domains = Domain.list( + apiclient=apiclient, + ) + if isinstance(domains, list) and len(domains) > 0: + return domains[0] + else: + raise Exception("Failed to find any domains") + +def get_zone(apiclient): + "Returns the default enabled zone" + + zones = Zone.list( + apiclient=apiclient, + ) + if isinstance(zones, list) and len(zones) > 0: + for zone in zones: + if zone.allocationstate == 'Enabled': + return zone + else: + raise Exception("No active zones found for deployment") + else: + raise Exception("Failed to find specified zone.") + +def get_service_offering(apiclient, storagetype='shared', scope=None): + """Returns the service offering that is available in the zone + + @param: `storagetype` is assumed to be `shared storage` + @param: `scope` zone-wide or cluster-wide. defaults to cluster + """ + serviceofferings = ServiceOffering.list( + apiclient=apiclient, + name='Small Instance' + ) + if isinstance(serviceofferings, list) and len(serviceofferings) > 0: + for service in serviceofferings: + if service.storagetype == storagetype: + return service + raise Exception("No service offering for storagetype %s available") + +def get_template(apiclient, description=None): + "Returns a featured template with a specific description" + templates = Template.list( + apiclient=apiclient, + templatefilter='featured' + ) + + if isinstance(templates, list) and len(templates) > 0: + for template in templates: + if template.isready: + return template + else: + raise Exception("None of the templates are ready in your deployment") + else: + raise Exception("Failed to find ready and featured template of : %s" % description)