# Copyright (c) 2013 Mirantis Inc. # # Licensed 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 mock from sahara.conductor import resource as r from sahara.service.heat import templates as h from sahara.tests.unit import base from sahara.tests.unit import testutils as tu class TestClusterTemplate(base.SaharaWithDbTestCase): """Checks valid structure of Resources section in generated Heat templates. 1. It checks templates generation with different OpenStack network installations: Neutron, NovaNetwork with floating Ip auto assignment set to True or False. 2. Cinder volume attachments. 3. Basic instances creations with multi line user data provided. 4. Anti-affinity feature with proper nova scheduler hints included into Heat templates. """ def _make_node_groups(self, floating_ip_pool=None, volume_type=None): ng1 = tu.make_ng_dict('master', 42, ['namenode'], 1, floating_ip_pool=floating_ip_pool, image_id=None, volumes_per_node=0, volumes_size=0, id="1", image_username='root', volume_type=None) ng2 = tu.make_ng_dict('worker', 42, ['datanode'], 1, floating_ip_pool=floating_ip_pool, image_id=None, volumes_per_node=2, volumes_size=10, id="2", image_username='root', volume_type=volume_type) return ng1, ng2 def _make_cluster(self, mng_network, ng1, ng2, anti_affinity=[]): return tu.create_cluster("cluster", "tenant1", "general", "1.2.1", [ng1, ng2], user_keypair_id='user_key', neutron_management_network=mng_network, default_image_id='1', image_id=None, anti_affinity=anti_affinity) def _make_heat_template(self, cluster, ng1, ng2): heat_template = h.ClusterStack(cluster) heat_template.add_node_group_extra(ng1['id'], 1, get_ud_generator('line1\nline2')) heat_template.add_node_group_extra(ng2['id'], 1, get_ud_generator('line2\nline3')) return heat_template def test_get_anti_affinity_scheduler_hints(self): ng1, ng2 = self._make_node_groups('floating') cluster = self._make_cluster('private_net', ng1, ng2, anti_affinity=["datanode"]) heat_template = self._make_heat_template(cluster, ng1, ng2) ng1 = [ng for ng in cluster.node_groups if ng.name == "master"][0] ng2 = [ng for ng in cluster.node_groups if ng.name == "worker"][0] expected = {"scheduler_hints": {"group": {"Ref": "cluster-aa-group"}}} actual = heat_template._get_anti_affinity_scheduler_hints(ng2) self.assertEqual(expected, actual) expected = {} actual = heat_template._get_anti_affinity_scheduler_hints(ng1) self.assertEqual(expected, actual) def test_get_security_groups(self): ng1, ng2 = self._make_node_groups('floating') ng1['security_groups'] = ['1', '2'] ng2['security_groups'] = ['3', '4'] ng2['auto_security_group'] = True cluster = self._make_cluster('private_net', ng1, ng2) heat_template = self._make_heat_template(cluster, ng1, ng2) ng1 = [ng for ng in cluster.node_groups if ng.name == "master"][0] ng2 = [ng for ng in cluster.node_groups if ng.name == "worker"][0] expected = ['1', '2'] actual = heat_template._get_security_groups(ng1) self.assertEqual(expected, actual) expected = ['3', '4', {'Ref': 'cluster-worker-2'}] actual = heat_template._get_security_groups(ng2) self.assertEqual(expected, actual) def _generate_auto_security_group_template(self, use_neutron): self.override_config('use_neutron', use_neutron) ng1, ng2 = self._make_node_groups('floating') cluster = self._make_cluster('private_net', ng1, ng2) ng1['cluster'] = cluster ng2['cluster'] = cluster ng1 = r.NodeGroupResource(ng1) ng2 = r.NodeGroupResource(ng2) heat_template = self._make_heat_template(cluster, ng1, ng2) return heat_template._serialize_auto_security_group(ng1) @mock.patch('sahara.utils.openstack.neutron.get_private_network_cidrs') def test_serialize_auto_security_group_neutron(self, patched): ipv4_cidr = '192.168.0.0/24' ipv6_cidr = 'fe80::/64' patched.side_effect = lambda cluster: [ipv4_cidr, ipv6_cidr] expected_rules = [ ('0.0.0.0/0', 'IPv4', 'tcp', '22', '22'), ('::/0', 'IPv6', 'tcp', '22', '22'), (ipv4_cidr, 'IPv4', 'tcp', '1', '65535'), (ipv4_cidr, 'IPv4', 'udp', '1', '65535'), (ipv4_cidr, 'IPv4', 'icmp', '0', '255'), (ipv6_cidr, 'IPv6', 'tcp', '1', '65535'), (ipv6_cidr, 'IPv6', 'udp', '1', '65535'), (ipv6_cidr, 'IPv6', 'icmp', '0', '255'), ] expected = {'cluster-master-1': { 'type': 'OS::Neutron::SecurityGroup', 'properties': { 'description': 'Auto security group created by Sahara ' 'for Node Group \'master\' of cluster \'cluster\'.', 'rules': [{ 'remote_ip_prefix': rule[0], 'ethertype': rule[1], 'protocol': rule[2], 'port_range_min': rule[3], 'port_range_max': rule[4] } for rule in expected_rules] } }} actual = self._generate_auto_security_group_template(True) self.assertEqual(expected, actual) def test_serialize_auto_security_group_nova_network(self): expected = {'cluster-master-1': { 'type': 'AWS::EC2::SecurityGroup', 'properties': { 'GroupDescription': 'Auto security group created by Sahara' ' for Node Group \'master\' of cluster \'cluster\'.', 'SecurityGroupIngress': [{ 'ToPort': '22', 'CidrIp': '0.0.0.0/0', 'FromPort': '22', 'IpProtocol': 'tcp' }, { 'ToPort': '22', 'CidrIp': '::/0', 'FromPort': '22', 'IpProtocol': 'tcp' }] } }} actual = self._generate_auto_security_group_template(False) self.assertEqual(expected, actual) def get_ud_generator(s): def generator(*args, **kwargs): return s return generator