# Copyright 2014 # The Cloudscaling Group, 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 netaddr import tempest.cloudscaling.thirdparty.scenario.aws_compat.base as aws_base from tempest.lib.common.utils import data_utils from tempest.lib.common.utils.linux import remote_client from tempest import test import logging logging.getLogger('boto').setLevel(logging.CRITICAL) class VPC_Scenario(aws_base.BaseAWSTest): """ Reproduce 'VPC with Public and Private Subnets' scenario (http://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/VPC_Scenario2.html) """ class Context(object): vpc = None internet_gateway = None web_subnet = None db_subnet = None main_route_table = None custom_route_table = None web_security_group = None nat_security_group = None db_security_group = None web_instance = None db_instance = None nat_instance = None @classmethod @test.safe_setup def setUpClass(cls): super(VPC_Scenario, cls).setUpClass() cls.ctx = cls.Context() cls.zone = cls.config.boto.aws_zone cfg = cls.config.cloudscaling cls.ssh_user = cfg.general_ssh_user_name cls.vpc_cidr = netaddr.IPNetwork(cfg.vpc_cidr) cls.web_subnet, cls.db_subnet = cls.vpc_cidr.subnet( cfg.vpc_subnet_prefix, 2) cls.test_client_cidr = netaddr.IPNetwork(cfg.test_client_cidr) cls.image_id = cls._prepare_image_id(cfg.general_image_name) cls.keypair = cls._prepare_key_pair() @classmethod def tearDownClass(cls): if cls.ctx is not None: for group in [cls.ctx.web_security_group, cls.ctx.nat_security_group, cls.ctx.db_security_group]: if not group: continue try: cls._revoke_security_group_linked_rules(group) except Exception: pass super(VPC_Scenario, cls).tearDownClass() @classmethod def _revoke_security_group_linked_rules(cls, group): groups = cls.vpc_client.get_all_security_groups(group_ids=[group.id]) if len(groups) == 0: return sg = groups[0] for rule in sg.rules: for grant in rule.grants: if not grant.cidr_ip: cls.vpc_client.revoke_security_group( group_id=sg.id, ip_protocol=rule.ip_protocol, from_port=rule.from_port, to_port=rule.to_port, src_security_group_group_id=grant.groupId) for rule in sg.rules_egress: for grant in rule.grants: if not grant.cidr_ip: cls.vpc_client.revoke_security_group_egress( sg.id, rule.ip_protocol, from_port=rule.from_port, to_port=rule.to_port, src_group_id=grant.groupId) def test_000_create_vpc(self): """Create VPC""" vpc = self.vpc_client.create_vpc(str(self.vpc_cidr)) self.assertIsNotNone(vpc) self.assertTrue(vpc.id) self.addResourceCleanUp(self.vpc_client.delete_vpc, vpc.id) self.ctx.vpc = vpc def test_001_create_internet_gateway(self): """Create internet gateway""" ig = self.vpc_client.create_internet_gateway() self.assertIsNotNone(ig) self.assertTrue(ig.id) self.addResourceCleanUp(self._destroy_internet_gateway, ig) status = self.vpc_client.attach_internet_gateway(ig.id, self.ctx.vpc.id) self.assertTrue(status) self.ctx.internet_gateway = ig def test_010_create_subnets(self): """Create subnets""" sn = self.vpc_client.create_subnet(self.ctx.vpc.id, str(self.web_subnet), self.zone) self.assertIsNotNone(sn) self.assertTrue(sn.id) self.addResourceCleanUp(self.vpc_client.delete_subnet, sn.id) self.ctx.web_subnet = sn sn = self.vpc_client.create_subnet(self.ctx.vpc.id, str(self.db_subnet), self.zone) self.assertIsNotNone(sn) self.assertTrue(sn.id) self.addResourceCleanUp(self.vpc_client.delete_subnet, sn.id) self.ctx.db_subnet = sn def test_020_get_main_route_table(self): """Describe auto created route table""" rtables = self.vpc_client.get_all_route_tables( filters=[("vpc-id", self.ctx.vpc.id)]) self.assertIsNotNone(rtables) self.assertEqual(1, len(rtables)) self.ctx.main_route_table = rtables[0] def test_025_create_custom_route_table(self): """Create route table for web servers""" rtable = self.vpc_client.create_route_table(self.ctx.vpc.id) self.assertIsNotNone(rtable) self.assertTrue(rtable.id) self.addResourceCleanUp(self.vpc_client.delete_route_table, rtable.id) ig = self.ctx.internet_gateway status = self.vpc_client.create_route(rtable.id, "0.0.0.0/0", gateway_id=ig.id) self.assertTrue(status) association_id = self.vpc_client.associate_route_table( rtable.id, self.ctx.web_subnet.id) self.assertTrue(association_id) self.addResourceCleanUp(self.vpc_client.disassociate_route_table, association_id) self.ctx.custom_route_table = rtable def test_050_create_security_groups(self): """Create and tune security groups""" sg = self.vpc_client.create_security_group( data_utils.rand_name("WebServerSG-"), data_utils.rand_name("description "), self.ctx.vpc.id) self.assertIsNotNone(sg) self.assertTrue(sg.id) self.addResourceCleanUp(self._destroy_security_group_wait, sg) self.ctx.web_security_group = sg sg = self.vpc_client.create_security_group( data_utils.rand_name("NATSG-"), data_utils.rand_name("description "), self.ctx.vpc.id) self.assertIsNotNone(sg) self.assertTrue(sg.id) self.addResourceCleanUp(self._destroy_security_group_wait, sg) self.ctx.nat_security_group = sg sg = self.vpc_client.create_security_group( data_utils.rand_name("DBServerSG-"), data_utils.rand_name("description "), self.ctx.vpc.id) self.assertIsNotNone(sg) self.assertTrue(sg.id) self.addResourceCleanUp(self._destroy_security_group_wait, sg) self.ctx.db_security_group = sg sg = self.ctx.web_security_group status = self.vpc_client.revoke_security_group_egress( sg.id, "-1", cidr_ip="0.0.0.0/0") self.assertTrue(status) status = self.vpc_client.authorize_security_group_egress( sg.id, "tcp", 1433, 1433, src_group_id=self.ctx.db_security_group.id) self.assertTrue(status) status = self.vpc_client.authorize_security_group_egress( sg.id, "tcp", 3306, 3306, src_group_id=self.ctx.db_security_group.id) self.assertTrue(status) # NOTE(ft): especially for connectivity test status = self.vpc_client.authorize_security_group_egress( sg.id, "tcp", 80, 80, cidr_ip="0.0.0.0/0") self.assertTrue(status) # NOTE(ft): especially for connectivity test status = self.vpc_client.authorize_security_group_egress( sg.id, "tcp", 22, 22, src_group_id=self.ctx.db_security_group.id) self.assertTrue(status) status = self.vpc_client.authorize_security_group( group_id=sg.id, ip_protocol="tcp", from_port=80, to_port=80, cidr_ip="0.0.0.0/0") self.assertTrue(status) status = self.vpc_client.authorize_security_group( group_id=sg.id, ip_protocol="tcp", from_port=443, to_port=443, cidr_ip="0.0.0.0/0") self.assertTrue(status) status = self.vpc_client.authorize_security_group( group_id=sg.id, ip_protocol="tcp", from_port=22, to_port=22, cidr_ip=str(self.test_client_cidr)) self.assertTrue(status) status = self.vpc_client.authorize_security_group( group_id=sg.id, ip_protocol="tcp", from_port=3389, to_port=3389, cidr_ip=str(self.test_client_cidr)) self.assertTrue(status) sg = self.ctx.nat_security_group status = self.vpc_client.revoke_security_group_egress( sg.id, "-1", cidr_ip="0.0.0.0/0") self.assertTrue(status) status = self.vpc_client.authorize_security_group_egress( sg.id, "tcp", 80, 80, cidr_ip="0.0.0.0/0") self.assertTrue(status) status = self.vpc_client.authorize_security_group_egress( sg.id, "tcp", 443, 443, cidr_ip="0.0.0.0/0") self.assertTrue(status) status = self.vpc_client.authorize_security_group( group_id=sg.id, ip_protocol="tcp", from_port=80, to_port=80, cidr_ip=str(self.db_subnet)) self.assertTrue(status) status = self.vpc_client.authorize_security_group( group_id=sg.id, ip_protocol="tcp", from_port=443, to_port=443, cidr_ip=str(self.db_subnet)) self.assertTrue(status) status = self.vpc_client.authorize_security_group( group_id=sg.id, ip_protocol="tcp", from_port=22, to_port=22, cidr_ip=str(self.test_client_cidr)) self.assertTrue(status) sg = self.ctx.db_security_group status = self.vpc_client.revoke_security_group_egress( sg.id, "-1", cidr_ip="0.0.0.0/0") self.assertTrue(status) status = self.vpc_client.authorize_security_group_egress( sg.id, "tcp", 80, 80, cidr_ip="0.0.0.0/0") self.assertTrue(status) status = self.vpc_client.authorize_security_group_egress( sg.id, "tcp", 443, 443, cidr_ip="0.0.0.0/0") self.assertTrue(status) status = self.vpc_client.authorize_security_group( group_id=sg.id, ip_protocol="tcp", from_port=1433, to_port=1433, src_security_group_group_id=self.ctx.web_security_group.id) self.assertTrue(status) status = self.vpc_client.authorize_security_group( group_id=sg.id, ip_protocol="tcp", from_port=3306, to_port=3306, src_security_group_group_id=self.ctx.web_security_group.id) self.assertTrue(status) # NOTE(ft): especially for connectivity test status = self.vpc_client.authorize_security_group( group_id=sg.id, ip_protocol="tcp", from_port=22, to_port=22, src_security_group_group_id=self.ctx.web_security_group.id) self.assertTrue(status) def test_100_launch_nat_instance(self): """Launch instances for NAT server""" reservation = self.vpc_client.run_instances( self.image_id, key_name=self.keypair.name, security_group_ids=[self.ctx.nat_security_group.id], instance_type=self.instance_type, placement=self.zone, subnet_id=self.ctx.web_subnet.id) self.assertIsNotNone(reservation) self.addResourceCleanUp(self.destroy_reservation, reservation) self.assertEqual(1, len(reservation.instances)) instance = reservation.instances[0] if instance.state != "running": self.assertInstanceStateWait(instance, "running") self._prepare_public_ip(instance) status = self.vpc_client.modify_instance_attribute( instance.id, 'sourceDestCheck', False) self.assertTrue(status) rtable = self.ctx.main_route_table status = self.vpc_client.create_route(rtable.id, "0.0.0.0/0", instance_id=instance.id) self.assertTrue(status) self.ctx.nat_instance = instance def test_101_launch_instances(self): """Launch instances for web server and db server""" reservation = self.vpc_client.run_instances( self.image_id, key_name=self.keypair.name, security_group_ids=[self.ctx.web_security_group.id], instance_type=self.instance_type, placement=self.zone, subnet_id=self.ctx.web_subnet.id) self.assertIsNotNone(reservation) self.addResourceCleanUp(self.destroy_reservation, reservation) self.assertEqual(1, len(reservation.instances)) instance = reservation.instances[0] if instance.state != "running": self.assertInstanceStateWait(instance, "running") self._prepare_public_ip(instance) self.ctx.web_instance = instance reservation = self.vpc_client.run_instances( self.image_id, key_name=self.keypair.name, security_group_ids=[self.ctx.db_security_group.id], instance_type=self.instance_type, placement=self.zone, subnet_id=self.ctx.db_subnet.id) self.assertIsNotNone(reservation) self.addResourceCleanUp(self.destroy_reservation, reservation) self.assertEqual(1, len(reservation.instances)) instance = reservation.instances[0] if instance.state != "running": self.assertInstanceStateWait(instance, "running") self.ctx.db_instance = instance def test_102_tune_nat_instance(self): """Tune NAT in NAT instance""" instance = self.ctx.nat_instance address = instance.ip_address ssh = remote_client.RemoteClient(address, self.ssh_user, pkey=self.keypair.material) # NOTE(ft): We must use tty mode, because some images (like Amazon # Linux) has restrictions (requiretty flag in /etc/sudoers) ssh_conn = ssh.ssh_client._get_ssh_connection() chan = ssh_conn.get_transport().open_session() chan.get_pty() chan.exec_command("sudo iptables -t nat -A POSTROUTING -s %s " "-o eth0 -j MASQUERADE" % str(self.vpc_cidr)) chan.close() chan = ssh_conn.get_transport().open_session() chan.get_pty() chan.exec_command("sudo sysctl -w net.ipv4.ip_forward=1") chan.close() ssh_conn.close() def test_200_check_connectivity(self): """Check inside and outside connectivities""" web_ip = self.ctx.web_instance.ip_address db_ip = self.ctx.db_instance.private_ip_address ssh = remote_client.RemoteClient(web_ip, self.ssh_user, pkey=self.keypair.material) ssh.exec_command("curl -s http://google.com") ssh_conn = ssh.ssh_client._get_ssh_connection() sftp = ssh_conn.open_sftp() fr = sftp.file("key.pem", 'wb') fr.set_pipelined(True) fr.write(self.keypair.material) fr.close() ssh_conn.close() ssh.exec_command('chmod 400 key.pem') ssh.exec_command("ssh -i key.pem -o UserKnownHostsFile=/dev/null " "-o StrictHostKeyChecking=no %(user)s@%(ip)s " "curl -s http://google.com" % {"user": self.ssh_user, "ip": db_ip})