From ba37e5c35315f963cd0a14912181c4b71d99b6c6 Mon Sep 17 00:00:00 2001 From: Nikolay Mahotkin Date: Thu, 22 Dec 2016 14:43:47 +0300 Subject: [PATCH] Adding --exclude-secgroup option * There is a use case when we don't need to export security groups at all Change-Id: Ib531404d3079c4aef32df367274468da53912fc4 --- README.rst | 7 +- flameclient/client.py | 6 +- flameclient/cmd.py | 7 +- flameclient/flame.py | 25 ++- flameclient/tests/test_flame.py | 340 +++++++++++++++++++++++++++++++- 5 files changed, 369 insertions(+), 16 deletions(-) diff --git a/README.rst b/README.rst index c7ed0a1..f11ebce 100644 --- a/README.rst +++ b/README.rst @@ -1,5 +1,5 @@ Flame: Automatic Heat template generation -============================================ +========================================= OpenStack Orchestration project Heat implements an orchestration engine to launch multiple composite cloud applications based on templates. A Heat @@ -30,7 +30,7 @@ Then just run: python setup.py install Usage ----------------------- +----- usage: flame [-h] [--username USERNAME] [--password PASSWORD] [--project PROJECT] [--region REGION] [--auth_url AUTH_URL] @@ -38,7 +38,7 @@ Usage [--os-cert ] [--os-key ] [--endpoint_type ENDPOINT_TYPE] [--exclude-servers] [--exclude-volumes] [--exclude-keypairs] [--generate-stack-data] - [--extract-ports] + [--extract-ports] [--exclude-secgroup] Heat template and data file generator @@ -71,6 +71,7 @@ Usage In addition to template, generate Heat stack data file. --extract-ports Export the tenant network ports + --exclude-secgroups Do not export in template security group resources Usage example ------------- diff --git a/flameclient/client.py b/flameclient/client.py index 6d514ad..d342519 100644 --- a/flameclient/client.py +++ b/flameclient/client.py @@ -34,12 +34,14 @@ class Client(object): **kwargs) def generate(self, exclude_servers, exclude_volumes, exclude_keypairs, - generate_stack_data, extract_ports=False): + generate_stack_data, extract_ports=False, + exclude_secgroups=False): self.template_generator.extract_vm_details(exclude_servers, exclude_volumes, exclude_keypairs, generate_stack_data, - extract_ports + extract_ports, + exclude_secgroups ) self.template_generator.extract_data() return self.template_generator.heat_template_and_data() diff --git a/flameclient/cmd.py b/flameclient/cmd.py index fde8276..4f4c230 100644 --- a/flameclient/cmd.py +++ b/flameclient/cmd.py @@ -91,6 +91,10 @@ def main(args=None): parser.add_argument('--extract-ports', action='store_true', default=False, help="Export the tenant network ports") + parser.add_argument('--exclude-secgroups', action='store_true', + default=False, + help="Do not export in template " + "security group resources") args = parser.parse_args() flame = client.Client(args.username, args.password, @@ -105,7 +109,8 @@ def main(args=None): args.exclude_volumes, args.exclude_keypairs, args.generate_stack_data, - args.extract_ports) + args.extract_ports, + args.exclude_secgroups) template.extract_data() print("### Heat Template ###") print(template.heat_template_and_data()) diff --git a/flameclient/flame.py b/flameclient/flame.py index 49cd611..f7af90a 100644 --- a/flameclient/flame.py +++ b/flameclient/flame.py @@ -172,23 +172,28 @@ class TemplateGenerator(object): def extract_vm_details(self, exclude_servers, exclude_volumes, exclude_keypairs, generate_data, - extract_ports=False): + extract_ports=False, exclude_secgroups=False): self.exclude_servers = exclude_servers self.exclude_volumes = exclude_volumes self.exclude_keypairs = exclude_keypairs self.generate_data = generate_data self.extract_ports = extract_ports + self.exclude_secgroups = exclude_secgroups self.external_networks = [] fetch_map = { 'subnets': (self.neutron.subnet_list, self.build_data), 'networks': (self.neutron.network_list, self.build_data), 'routers': (self.neutron.router_list, lambda x: x), - 'secgroups': (self.neutron.secgroup_list, self.build_data), 'servergroups': (self.nova.servergroup_list, self.build_data), 'floatingips': (self.neutron.floatingip_list, lambda x: x), 'ports': (self.neutron.port_list, self.build_data), } + if not exclude_secgroups: + fetch_map['secgroups'] = ( + self.neutron.secgroup_list, self.build_data + ) + if not exclude_keypairs: fetch_map['keys'] = (self.nova.keypair_list, lambda l: {key.name: (index, key) for @@ -378,8 +383,9 @@ class TemplateGenerator(object): properties['name'] = port['name'] resource = Resource("port_%d" % n, 'OS::Neutron::Port', port['id'], properties) - security_groups = self.build_port_secgroups(resource, port) - properties['security_groups'] = security_groups + if not self.exclude_secgroups: + security_groups = self.build_port_secgroups(resource, port) + properties['security_groups'] = security_groups resources.append(resource) resources_dict[port['id']] = resource @@ -568,9 +574,10 @@ class TemplateGenerator(object): if ports: properties['networks'] = ports else: - security_groups = self.build_secgroups(resource, server) - if security_groups: - properties['security_groups'] = security_groups + if not self.exclude_secgroups: + security_groups = self.build_secgroups(resource, server) + if security_groups: + properties['security_groups'] = security_groups networks = self.build_networks(server.addresses) if networks: @@ -724,10 +731,12 @@ class TemplateGenerator(object): resources += self._extract_ports() resources += self._extract_subnets() - resources += self._extract_secgroups() resources += self._extract_floating() resources += self._extract_servergroups() + if not self.exclude_secgroups: + resources += self._extract_secgroups() + if not self.exclude_keypairs: resources += self._extract_keys() if not self.exclude_servers: diff --git a/flameclient/tests/test_flame.py b/flameclient/tests/test_flame.py index 4761ec5..dbca7b7 100644 --- a/flameclient/tests/test_flame.py +++ b/flameclient/tests/test_flame.py @@ -275,12 +275,13 @@ class BaseTestCase(base.TestCase): super(BaseTestCase, self).tearDown() def get_generator(self, exclude_servers, exclude_volumes, - exclude_keypairs, generate_data, extract_ports): + exclude_keypairs, generate_data, + extract_ports, exclude_secgroups=False): generator = flame.TemplateGenerator('x', 'x', 'x', 'x', True, 'publicURL') generator.extract_vm_details(exclude_servers, exclude_volumes, exclude_keypairs, generate_data, - extract_ports) + extract_ports, exclude_secgroups) return generator def check_stackdata(self, resources, expected_resources): @@ -2102,6 +2103,341 @@ class GenerationTests(BaseTestCase): self.assertEqual(generator.template['parameters'], expected_parameters) self.assertEqual(generator.stack_data['resources'], expected_data) + def test_generation_secgroups(self): + self.mock_neutron().groups = [{ + 'id': '4321', + 'name': 'my_sec_group', + 'security_group_rules': [{ + 'protocol': 'TCP', + 'port_range_min': '22', + 'port_range_max': '22', + 'tenant_id': 'tenant', + 'id': '1212', + 'security_group_id': '4444', + 'remote_group_id': None + }], + 'description': 'group description' + }] + + generator = self.get_generator(False, False, False, True, False, False) + + expected_resources = { + "volume_0": { + "properties": { + "name": "vol1", + "volume_type": { + "get_param": "volume_0_volume_type" + }, + "description": "Description", + "size": 1 + }, + "type": "OS::Cinder::Volume" + }, + "key_0": { + "type": "OS::Nova::KeyPair", + "properties": { + "name": "testkey", + "public_key": "ssh-rsa XXXX" + } + }, + "network_0": { + "properties": { + "admin_state_up": True, + "shared": False, + "name": "mynetwork" + }, + "type": "OS::Neutron::Net" + }, + "router_0": { + "properties": { + "admin_state_up": "true", + "name": "myrouter" + }, + "type": "OS::Neutron::Router" + }, + "server_0": { + "properties": { + "image": { + "get_param": "server_0_image" + }, + "flavor": { + "get_param": "server_0_flavor" + }, + "name": "server1", + "diskConfig": "MANUAL", + "key_name": { + "get_resource": "key_0" + } + }, + "type": "OS::Nova::Server" + }, + "security_group_0": { + "type": "OS::Neutron::SecurityGroup", + "properties": { + "rules": [ + { + "port_range_min": "22", + "port_range_max": "22", + "protocol": "TCP" + } + ], + "name": "my_sec_group", + "description": "group description" + } + }, + "servergroup_0": { + "type": "OS::Nova::ServerGroup", + "properties": { + "policies": "affinity", + "name": "policy_group" + } + } + } + + expected_parameters = { + "server_0_image": { + "description": "Image to use to boot server server_0", + "type": "string", + "default": "3333" + }, + "server_0_flavor": { + "description": "Flavor to use for server server_0", + "type": "string", + "default": "m1.small" + }, + "volume_0_volume_type": { + "default": "fast", + "description": "Volume type for volume volume_0", + "type": "string" + } + } + expected_data = { + 'key_0': { + 'action': 'CREATE', + 'metadata': {}, + 'name': 'key_0', + 'resource_data': {}, + 'resource_id': 'key', + 'status': 'COMPLETE', + 'type': 'OS::Nova::KeyPair' + }, + 'network_0': { + 'action': 'CREATE', + 'metadata': {}, + 'name': 'network_0', + 'resource_data': {}, + 'resource_id': '2222', + 'status': 'COMPLETE', + 'type': 'OS::Neutron::Net' + }, + 'router_0': { + 'action': 'CREATE', + 'metadata': {}, + 'name': 'router_0', + 'resource_data': {}, + 'resource_id': '1234', + 'status': 'COMPLETE', + 'type': 'OS::Neutron::Router' + }, + 'security_group_0': { + 'action': 'CREATE', + 'metadata': {}, + 'name': 'security_group_0', + 'resource_data': {}, + 'resource_id': '4321', + 'status': 'COMPLETE', + 'type': 'OS::Neutron::SecurityGroup' + }, + 'server_0': { + 'action': 'CREATE', + 'metadata': {}, + 'name': 'server_0', + 'resource_data': {}, + 'resource_id': '1234', + 'status': 'COMPLETE', + 'type': 'OS::Nova::Server' + }, + 'servergroup_0': { + 'action': 'CREATE', + 'metadata': {}, + 'name': 'servergroup_0', + 'resource_data': {}, + 'resource_id': '1234', + 'status': 'COMPLETE', + 'type': 'OS::Nova::ServerGroup' + }, + 'volume_0': { + 'action': 'CREATE', + 'metadata': {}, + 'name': 'volume_0', + 'resource_data': {}, + 'resource_id': 1234, + 'status': 'COMPLETE', + 'type': 'OS::Cinder::Volume' + } + } + + generator.extract_data() + self.assertEqual(expected_resources, generator.template['resources']) + self.assertEqual(expected_parameters, generator.template['parameters']) + self.assertEqual(expected_data, generator.stack_data['resources']) + + def test_generation_exclude_secgroups(self): + self.mock_neutron().groups = [{ + 'id': '4321', + 'name': 'my_sec_group', + 'security_group_rules': [{ + 'protocol': 'TCP', + 'port_range_min': '22', + 'port_range_max': '22', + 'tenant_id': 'tenant', + 'id': '1212', + 'security_group_id': '4444', + 'remote_group_id': None + }], + 'description': 'group description' + }] + + generator = self.get_generator(False, False, False, True, False, True) + + expected_resources = { + "volume_0": { + "properties": { + "name": "vol1", + "volume_type": { + "get_param": "volume_0_volume_type" + }, + "description": "Description", + "size": 1 + }, + "type": "OS::Cinder::Volume" + }, + "key_0": { + "type": "OS::Nova::KeyPair", + "properties": { + "name": "testkey", + "public_key": "ssh-rsa XXXX" + } + }, + "network_0": { + "properties": { + "admin_state_up": True, + "shared": False, + "name": "mynetwork" + }, + "type": "OS::Neutron::Net" + }, + "router_0": { + "properties": { + "admin_state_up": "true", + "name": "myrouter" + }, + "type": "OS::Neutron::Router" + }, + "server_0": { + "properties": { + "image": { + "get_param": "server_0_image" + }, + "flavor": { + "get_param": "server_0_flavor" + }, + "name": "server1", + "diskConfig": "MANUAL", + "key_name": { + "get_resource": "key_0" + } + }, + "type": "OS::Nova::Server" + }, + "servergroup_0": { + "type": "OS::Nova::ServerGroup", + "properties": { + "policies": "affinity", + "name": "policy_group" + } + } + } + + expected_parameters = { + "server_0_image": { + "description": "Image to use to boot server server_0", + "type": "string", + "default": "3333" + }, + "server_0_flavor": { + "description": "Flavor to use for server server_0", + "type": "string", + "default": "m1.small" + }, + "volume_0_volume_type": { + "default": "fast", + "description": "Volume type for volume volume_0", + "type": "string" + } + } + expected_data = { + 'key_0': { + 'action': 'CREATE', + 'metadata': {}, + 'name': 'key_0', + 'resource_data': {}, + 'resource_id': 'key', + 'status': 'COMPLETE', + 'type': 'OS::Nova::KeyPair' + }, + 'network_0': { + 'action': 'CREATE', + 'metadata': {}, + 'name': 'network_0', + 'resource_data': {}, + 'resource_id': '2222', + 'status': 'COMPLETE', + 'type': 'OS::Neutron::Net' + }, + 'router_0': { + 'action': 'CREATE', + 'metadata': {}, + 'name': 'router_0', + 'resource_data': {}, + 'resource_id': '1234', + 'status': 'COMPLETE', + 'type': 'OS::Neutron::Router' + }, + 'server_0': { + 'action': 'CREATE', + 'metadata': {}, + 'name': 'server_0', + 'resource_data': {}, + 'resource_id': '1234', + 'status': 'COMPLETE', + 'type': 'OS::Nova::Server' + }, + 'servergroup_0': { + 'action': 'CREATE', + 'metadata': {}, + 'name': 'servergroup_0', + 'resource_data': {}, + 'resource_id': '1234', + 'status': 'COMPLETE', + 'type': 'OS::Nova::ServerGroup' + }, + 'volume_0': { + 'action': 'CREATE', + 'metadata': {}, + 'name': 'volume_0', + 'resource_data': {}, + 'resource_id': 1234, + 'status': 'COMPLETE', + 'type': 'OS::Cinder::Volume' + } + } + + generator.extract_data() + self.assertEqual(expected_resources, generator.template['resources']) + self.assertEqual(expected_parameters, generator.template['parameters']) + self.assertEqual(expected_data, generator.stack_data['resources']) + def test_generation_exclude_servers(self): generator = self.get_generator(True, False, False, True, False)