rhtyd closed pull request #2190: CLOUDSTACK-10010: Fixed the negotiation of S2S 
VPN connections
URL: https://github.com/apache/cloudstack/pull/2190
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git a/systemvm/patches/debian/config/opt/cloud/bin/configure.py 
b/systemvm/patches/debian/config/opt/cloud/bin/configure.py
index 9efc07c7188..5b4b062dba2 100755
--- a/systemvm/patches/debian/config/opt/cloud/bin/configure.py
+++ b/systemvm/patches/debian/config/opt/cloud/bin/configure.py
@@ -566,7 +566,7 @@ def configure_ipsec(self, obj):
         file.addeq(" lifetime=%s" % self.convert_sec_to_h(obj['esp_lifetime']))
         file.addeq(" pfs=%s" % pfs)
         file.addeq(" keyingtries=2")
-        file.addeq(" auto=start")
+        file.addeq(" auto=route")
         if 'encap' not in obj:
             obj['encap']=False
         file.addeq(" forceencaps=%s" % CsHelper.bool_to_yn(obj['encap']))
@@ -582,10 +582,21 @@ def configure_ipsec(self, obj):
             logging.info("Configured vpn %s %s", leftpeer, rightpeer)
             CsHelper.execute("ipsec rereadsecrets")
 
-        # This will load the new config and start the connection when needed 
since auto=start in the config
+        # This will load the new config
         CsHelper.execute("ipsec reload")
         os.chmod(vpnsecretsfile, 0400)
 
+        # Check that the ipsec config is ready
+        for i in range(2):
+            result = CsHelper.execute('ipsec status vpn-%s | grep "%s"' % 
(rightpeer, peerlist.split(",", 1)[0]))
+            if len(result) > 0:
+                break
+            time.sleep(1)
+
+        # With 'auto=route', connections are established with an attempt to 
communicate over the S2S VPN
+        # Attempt to ping the other side to initialize the connection of the 
S2S VPN configuration
+        CsHelper.execute("timeout 0.5 ping -c 1 %s" % (peerlist.split("/", 
1)[0]))
+
     def convert_sec_to_h(self, val):
         hrs = int(val) / 3600
         return "%sh" % hrs
diff --git a/test/integration/smoke/test_vpc_vpn.py 
b/test/integration/smoke/test_vpc_vpn.py
index ddf76930050..9ed1d0109ac 100644
--- a/test/integration/smoke/test_vpc_vpn.py
+++ b/test/integration/smoke/test_vpc_vpn.py
@@ -57,6 +57,7 @@
 from nose.plugins.attrib import attr
 
 import logging
+import subprocess
 import time
 
 
@@ -1177,3 +1178,447 @@ def tearDownClass(cls):
             cleanup_resources(cls.apiclient, cls.cleanup)
         except Exception, e:
             raise Exception("Cleanup failed with %s" % e)
+
+
+class TestVPCSite2SiteVPNMultipleOptions(cloudstackTestCase):
+
+    @classmethod
+    def setUpClass(cls):
+        cls.logger = logging.getLogger('TestVPCSite2SiteVPNMultipleOptions')
+        cls.stream_handler = logging.StreamHandler()
+        cls.logger.setLevel(logging.DEBUG)
+        cls.logger.addHandler(cls.stream_handler)
+
+        testClient = super(TestVPCSite2SiteVPNMultipleOptions, 
cls).getClsTestClient()
+        cls.apiclient = testClient.getApiClient()
+        cls.services = Services().services
+
+        cls.zone = get_zone(cls.apiclient, testClient.getZoneForTests())
+        cls.domain = get_domain(cls.apiclient)
+
+        cls.compute_offering = ServiceOffering.create(
+            cls.apiclient,
+            cls.services["compute_offering"]
+        )
+
+        cls.account = Account.create(
+            cls.apiclient, services=cls.services["account"])
+
+        cls.hypervisor = testClient.getHypervisorInfo()
+
+        cls.logger.debug("Downloading Template: %s from: %s" % 
(cls.services["template"][
+                         cls.hypervisor.lower()], 
cls.services["template"][cls.hypervisor.lower()]["url"]))
+        cls.template = Template.register(cls.apiclient, 
cls.services["template"][cls.hypervisor.lower(
+        )], cls.zone.id, hypervisor=cls.hypervisor.lower(), 
account=cls.account.name, domainid=cls.domain.id)
+        cls.template.download(cls.apiclient)
+
+        if cls.template == FAILED:
+            assert False, "get_template() failed to return template"
+
+        cls.logger.debug("Successfully created account: %s, id: \
+                   %s" % (cls.account.name,
+                          cls.account.id))
+
+        cls.cleanup = [cls.account, cls.compute_offering]
+        return
+
+    def _get_ssh_client(self, virtual_machine, services, retries):
+        """ Setup ssh client connection and return connection
+        vm requires attributes public_ip, public_port, username, password """
+
+        try:
+            ssh_client = SshClient(
+                virtual_machine.public_ip,
+                services["virtual_machine"]["ssh_port"],
+                services["virtual_machine"]["username"],
+                services["virtual_machine"]["password"],
+                retries)
+
+        except Exception as e:
+            self.fail("Unable to create ssh connection: " % e)
+
+        self.assertIsNotNone(
+            ssh_client, "Failed to setup ssh connection to vm=%s on 
public_ip=%s" % (virtual_machine.name, virtual_machine.public_ip))
+
+        return ssh_client
+
+    def _create_natrule(self, vpc, vm, public_port, private_port, public_ip, 
network, services=None):
+        self.logger.debug("Creating NAT rule in network for vm with public IP")
+        if not services:
+            self.services["natrule"]["privateport"] = private_port
+            self.services["natrule"]["publicport"] = public_port
+            self.services["natrule"]["startport"] = public_port
+            self.services["natrule"]["endport"] = public_port
+            services = self.services["natrule"]
+
+        nat_rule = NATRule.create(
+            apiclient=self.apiclient,
+            services=services,
+            ipaddressid=public_ip.ipaddress.id,
+            virtual_machine=vm,
+            networkid=network.id
+        )
+        self.assertIsNotNone(
+            nat_rule, "Failed to create NAT Rule for %s" % 
public_ip.ipaddress.ipaddress)
+        self.logger.debug(
+            "Adding NetworkACL rules to make NAT rule accessible")
+
+        vm.ssh_ip = nat_rule.ipaddress
+        vm.public_ip = nat_rule.ipaddress
+        vm.public_port = int(public_port)
+        return nat_rule
+
+    def _validate_vpc_offering(self, vpc_offering):
+
+        self.logger.debug("Check if the VPC offering is created successfully?")
+        vpc_offs = VpcOffering.list(
+            self.apiclient,
+            id=vpc_offering.id
+        )
+        offering_list = validateList(vpc_offs)
+        self.assertEqual(offering_list[0],
+                         PASS,
+                         "List VPC offerings should return a valid list"
+                         )
+        self.assertEqual(
+            vpc_offering.name,
+            vpc_offs[0].name,
+            "Name of the VPC offering should match with listVPCOff data"
+        )
+        self.logger.debug(
+            "VPC offering is created successfully - %s" %
+            vpc_offering.name)
+        return
+
+    def _create_vpc_offering(self, offering_name):
+
+        vpc_off = None
+        if offering_name is not None:
+
+            self.logger.debug("Creating VPC offering: %s", offering_name)
+            vpc_off = VpcOffering.create(
+                self.apiclient,
+                self.services[offering_name]
+            )
+
+            self._validate_vpc_offering(vpc_off)
+            self.cleanup.append(vpc_off)
+
+        return vpc_off
+
+    @attr(tags=["advanced"], required_hardware="true")
+    def test_01_vpc_site2site_vpn_multiple_options(self):
+        """Test Site 2 Site VPN Across VPCs"""
+        self.logger.debug("Starting test: 
test_01_vpc_site2site_vpn_multiple_options")
+        # 0) Get the default network offering for VPC
+        networkOffering = NetworkOffering.list(
+            self.apiclient, 
name="DefaultIsolatedNetworkOfferingForVpcNetworks")
+        self.assert_(networkOffering is not None and len(
+            networkOffering) > 0, "No VPC based network offering")
+
+        # Create and Enable VPC offering
+        vpc_offering = self._create_vpc_offering('vpc_offering')
+        self.assert_(vpc_offering is not None, "Failed to create VPC Offering")
+        vpc_offering.update(self.apiclient, state='Enabled')
+
+        vpc1 = None
+        # Create VPC 1
+        try:
+            vpc1 = VPC.create(
+                apiclient=self.apiclient,
+                services=self.services["vpc"],
+                networkDomain="vpc1.vpn",
+                vpcofferingid=vpc_offering.id,
+                zoneid=self.zone.id,
+                account=self.account.name,
+                domainid=self.domain.id
+            )
+        except Exception as e:
+            self.fail(e)
+        finally:
+            self.assert_(vpc1 is not None, "VPC1 creation failed")
+
+        self.logger.debug("VPC1 %s created" % vpc1.id)
+
+        vpc2 = None
+        # Create VPC 2
+        try:
+            vpc2 = VPC.create(
+                apiclient=self.apiclient,
+                services=self.services["vpc2"],
+                networkDomain="vpc2.vpn",
+                vpcofferingid=vpc_offering.id,
+                zoneid=self.zone.id,
+                account=self.account.name,
+                domainid=self.domain.id
+            )
+        except Exception as e:
+            self.fail(e)
+        finally:
+            self.assert_(vpc2 is not None, "VPC2 creation failed")
+
+        self.logger.debug("VPC2 %s created" % vpc2.id)
+
+        default_acl = NetworkACLList.list(
+            self.apiclient, name="default_allow")[0]
+
+        ntwk1 = None
+        # Create network in VPC 1
+        try:
+            ntwk1 = Network.create(
+                apiclient=self.apiclient,
+                services=self.services["network_1"],
+                accountid=self.account.name,
+                domainid=self.account.domainid,
+                networkofferingid=networkOffering[0].id,
+                zoneid=self.zone.id,
+                vpcid=vpc1.id,
+                aclid=default_acl.id
+            )
+        except Exception as e:
+            self.fail(e)
+        finally:
+            self.assertIsNotNone(ntwk1, "Network failed to create")
+
+        self.logger.debug("Network %s created in VPC %s" % (ntwk1.id, vpc1.id))
+
+        ntwk2 = None
+        # Create network in VPC 2
+        try:
+            ntwk2 = Network.create(
+                apiclient=self.apiclient,
+                services=self.services["network_2"],
+                accountid=self.account.name,
+                domainid=self.account.domainid,
+                networkofferingid=networkOffering[0].id,
+                zoneid=self.zone.id,
+                vpcid=vpc2.id,
+                aclid=default_acl.id
+            )
+        except Exception as e:
+            self.fail(e)
+        finally:
+            self.assertIsNotNone(ntwk2, "Network failed to create")
+
+        self.logger.debug("Network %s created in VPC %s" % (ntwk2.id, vpc2.id))
+
+        vm1 = None
+        # Deploy a vm in network 2
+        try:
+            vm1 = VirtualMachine.create(self.apiclient, 
services=self.services["virtual_machine"],
+                                        templateid=self.template.id,
+                                        zoneid=self.zone.id,
+                                        accountid=self.account.name,
+                                        domainid=self.account.domainid,
+                                        
serviceofferingid=self.compute_offering.id,
+                                        networkids=ntwk1.id,
+                                        hypervisor=self.hypervisor
+                                        )
+        except Exception as e:
+            self.fail(e)
+        finally:
+            self.assert_(vm1 is not None, "VM failed to deploy")
+            self.assert_(vm1.state == 'Running', "VM is not running")
+
+        self.logger.debug("VM %s deployed in VPC %s" % (vm1.id, vpc1.id))
+
+        vm2 = None
+        # Deploy a vm in network 2
+        try:
+            vm2 = VirtualMachine.create(self.apiclient, 
services=self.services["virtual_machine"],
+                                        templateid=self.template.id,
+                                        zoneid=self.zone.id,
+                                        accountid=self.account.name,
+                                        domainid=self.account.domainid,
+                                        
serviceofferingid=self.compute_offering.id,
+                                        networkids=ntwk2.id,
+                                        hypervisor=self.hypervisor
+                                        )
+        except Exception as e:
+            self.fail(e)
+        finally:
+            self.assert_(vm2 is not None, "VM failed to deploy")
+            self.assert_(vm2.state == 'Running', "VM is not running")
+
+        self.debug("VM %s deployed in VPC %s" % (vm2.id, vpc2.id))
+
+        # default config
+        config = {
+            'ike_enc'       :'aes128',
+            'ike_hash'      :'sha1',
+            'ike_dh'        :'modp1536',
+            'esp_enc'       :'aes128',
+            'esp_hash'      :'sha1',
+            'esp_pfs'       :'modp1536',
+            'psk'           :'secreatKey',
+            'ike_life'      :86400,
+            'esp_life'      :3600,
+            'dpd'           :True,
+            'force_encap'   :False,
+            'passive_1'     :False,
+            'passive_2'     :False
+        }
+        test_confs = [
+            {}, # default
+            {'force_encap': True},
+            {'ike_life': ''},
+            {'esp_life': ''},
+            {'ike_life': '', 'esp_life': ''},
+            {'passive_1': True, 'passive_2': True},
+            {'passive_1': False, 'passive_2': True},
+            {'passive_1': True, 'passive_2': False},
+            {'passive_1': False, 'passive_2': False, 'dpd': False},
+            {'passive_1': True, 'passive_2': True, 'dpd': False},
+            {'passive_1': True, 'passive_2': False, 'dpd': False},
+            {'passive_1': False, 'passive_2': True, 'dpd': False},
+            {'passive_1': True, 'passive_2': False, 'esp_pfs': ''},
+            {'ike_dh': 'modp3072', 'ike_hash': 'sha256', 'esp_pfs': 
'modp2048', 'esp_hash':'sha384'},
+            {'ike_dh': 'modp4096', 'ike_hash': 'sha384', 'esp_pfs': 
'modp6144', 'esp_hash':'sha512'},
+            {'ike_dh': 'modp8192', 'ike_hash': 'sha512', 'esp_pfs': 
'modp8192', 'esp_hash':'sha384'}
+        ]
+
+        # 4) Enable Site-to-Site VPN for VPC
+        vpn1_response = Vpn.createVpnGateway(self.apiclient, vpc1.id)
+        self.assert_(
+            vpn1_response is not None, "Failed to enable VPN Gateway 1")
+        self.logger.debug("VPN gateway for VPC %s enabled" % vpc1.id)
+
+        vpn2_response = Vpn.createVpnGateway(self.apiclient, vpc2.id)
+        self.assert_(
+            vpn2_response is not None, "Failed to enable VPN Gateway 2")
+        self.logger.debug("VPN gateway for VPC %s enabled" % vpc2.id)
+
+        # 5) Add VPN Customer gateway info
+        src_nat_list = PublicIPAddress.list(
+            self.apiclient,
+            account=self.account.name,
+            domainid=self.account.domainid,
+            listall=True,
+            issourcenat=True,
+            vpcid=vpc1.id
+        )
+        ip1 = src_nat_list[0]
+        src_nat_list = PublicIPAddress.list(
+            self.apiclient,
+            account=self.account.name,
+            domainid=self.account.domainid,
+            listall=True,
+            issourcenat=True,
+            vpcid=vpc2.id
+        )
+        ip2 = src_nat_list[0]
+
+        # acquire an extra ip address to use to ssh into vm2
+        try:
+            services = self.services.copy()
+            del services["account"]
+            vm2.public_ip = PublicIPAddress.create(
+                apiclient=self.apiclient,
+                zoneid=self.zone.id,
+                account=self.account.name,
+                domainid=self.account.domainid,
+                services=services,
+                networkid=ntwk2.id,
+                vpcid=vpc2.id)
+        except Exception as e:
+            self.fail(e)
+        finally:
+            self.assert_(
+                vm2.public_ip is not None, "Failed to aqcuire public ip for 
vm2")
+
+        natrule = None
+        # Create port forward to be able to ssh into vm2
+        try:
+            natrule = self._create_natrule(
+                vpc2, vm2, 22, 22, vm2.public_ip, ntwk2)
+        except Exception as e:
+            self.fail(e)
+        finally:
+            self.assert_(
+                natrule is not None, "Failed to create portforward for vm2")
+            time.sleep(20)
+
+        # setup ssh connection to vm2
+        ssh_client = self._get_ssh_client(vm2, self.services, 10)
+        if not ssh_client:
+            self.fail("Failed to setup ssh connection to %s" % vm2.public_ip)
+
+        for test_c in test_confs:
+            c = config.copy()
+            c.update(test_c)
+            services = self._get_vpn_config(c)
+            self.logger.debug(services)
+            customer1_response = VpnCustomerGateway.create(
+                self.apiclient,
+                services,
+                "Peer VPC1",
+                ip1.ipaddress,
+                vpc1.cidr,
+                account=self.account.name,
+                domainid=self.account.domainid)
+            self.logger.debug("VPN customer gateway added for VPC %s enabled" 
% vpc1.id)
+            self.logger.debug(vars(customer1_response))
+
+            customer2_response = VpnCustomerGateway.create(
+                self.apiclient,
+                services,
+                "Peer VPC2",
+                ip2.ipaddress,
+                vpc2.cidr,
+                account=self.account.name,
+                domainid=self.account.domainid)
+            self.logger.debug("VPN customer gateway added for VPC %s enabled" 
% vpc2.id)
+            self.logger.debug(vars(customer2_response))
+
+            # 6) Connect two VPCs
+            vpnconn1_response = Vpn.createVpnConnection(
+                self.apiclient, customer1_response.id, vpn2_response['id'], 
c['passive_1'])
+            self.logger.debug("VPN connection created for VPC %s" % vpc2.id)
+            time.sleep(5)
+            vpnconn2_response = Vpn.createVpnConnection(
+                self.apiclient, customer2_response.id, vpn1_response['id'], 
c['passive_2'])
+            self.logger.debug("VPN connection created for VPC %s" % vpc1.id)
+
+            # Wait for config
+            time.sleep(15)
+            # Run ping test
+            packet_loss = ssh_client.execute(
+            "/bin/ping -c 3 -t 10 " + vm1.nic[0].ipaddress + " |grep 
packet|cut -d ' ' -f 7| cut -f1 -d'%'")[0]
+            self.logger.debug("Packet loss %s" % packet_loss)
+            self.assert_(int(packet_loss) == 0, "Ping did not succeed")
+
+            # Cleanup
+            Vpn.deleteVpnConnection(self.apiclient, vpnconn1_response['id'])
+            Vpn.deleteVpnConnection(self.apiclient, vpnconn2_response['id'])
+            cleanup_resources(self.apiclient, [customer1_response, 
customer2_response])
+            # Wait 130s for complete cleanup
+            time.sleep(130)
+
+    def _get_vpn_config(self, c):
+        ike_policy = '%s-%s;%s' % (c['ike_enc'], c['ike_hash'], c['ike_dh']) 
if c['ike_dh'] else '%s-%s' % (c['ike_enc'], c['ike_hash'])
+        esp_policy = '%s-%s;%s' % (c['esp_enc'], c['esp_hash'], c['esp_pfs']) 
if c['esp_pfs'] else '%s-%s' % (c['esp_enc'], c['esp_hash'])
+
+        out =  {
+            'ipsecpsk': c['psk'],
+            'ikepolicy':ike_policy,
+            'esppolicy':esp_policy,
+            'dpd':c['dpd'],
+            'forceencap':c['force_encap']
+        }
+
+        if c['ike_life']:
+            out['ikelifetime'] = c['ike_life']
+        if c['esp_life']:
+            out['esplifetime'] = c['esp_life']
+
+        return out
+
+    @classmethod
+    def tearDownClass(cls):
+        try:
+            try:
+                cls.template.delete(cls.apiclient)
+            except Exception: pass
+            cleanup_resources(cls.apiclient, cls.cleanup)
+        except Exception, e:
+            raise Exception("Cleanup failed with %s" % e)


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
us...@infra.apache.org


With regards,
Apache Git Services

Reply via email to