LegacyVpnTest.py: Script for VPN testing

Added/Modified the following:
1. Added acts/tests/google/net/LegacyVpnTest.py
   Automation for all VPN types
2. Modified acts/test_utils/net/connectivity_const.py
   to add VPN Profile enums

Bug: 30169640
Test: Verified on NYC MR2
Change-Id: If05098bc87eeda7d1c045b390c6946e3a54d0900
diff --git a/acts/framework/acts/test_utils/net/connectivity_const.py b/acts/framework/acts/test_utils/net/connectivity_const.py
new file mode 100644
index 0000000..3888e91
--- /dev/null
+++ b/acts/framework/acts/test_utils/net/connectivity_const.py
@@ -0,0 +1,100 @@
+#!/usr/bin/env python3.4
+#
+#   Copyright 2016 - Google
+#
+#   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 enum
+
+######################################################
+# ConnectivityManager.NetworkCallback events
+######################################################
+EVENT_NETWORK_CALLBACK = "NetworkCallback"
+
+# event types
+NETWORK_CB_PRE_CHECK = "PreCheck"
+NETWORK_CB_AVAILABLE = "Available"
+NETWORK_CB_LOSING = "Losing"
+NETWORK_CB_LOST = "Lost"
+NETWORK_CB_UNAVAILABLE = "Unavailable"
+NETWORK_CB_CAPABILITIES_CHANGED = "CapabilitiesChanged"
+NETWORK_CB_SUSPENDED = "Suspended"
+NETWORK_CB_RESUMED = "Resumed"
+NETWORK_CB_LINK_PROPERTIES_CHANGED = "LinkPropertiesChanged"
+NETWORK_CB_INVALID = "Invalid"
+
+# event data keys
+NETWORK_CB_KEY_ID = "id"
+NETWORK_CB_KEY_EVENT = "networkCallbackEvent"
+NETWORK_CB_KEY_MAX_MS_TO_LIVE = "maxMsToLive"
+NETWORK_CB_KEY_RSSI = "rssi"
+NETWORK_CB_KEY_INTERFACE_NAME = "interfaceName"
+
+# Constants for VPN connection status
+VPN_STATE_DISCONNECTED = 0
+VPN_STATE_INITIALIZING = 1
+VPN_STATE_CONNECTING = 2
+VPN_STATE_CONNECTED = 3
+VPN_STATE_TIMEOUT = 4
+VPN_STATE_FAILED = 5
+# TODO gmoturu: determine the exact timeout value
+# This is a random value as of now
+VPN_TIMEOUT = 15
+
+# Constants for VpnProfile
+class VpnProfile(object):
+    """ This class contains all the possible
+        parameters required for VPN connection
+    """
+    NAME = "name"
+    TYPE = "type"
+    SERVER = "server"
+    USER = "username"
+    PWD = "password"
+    DNS = "dnsServers"
+    SEARCH_DOMAINS = "searchDomains"
+    ROUTES = "routes"
+    MPPE = "mppe"
+    L2TP_SECRET = "l2tpSecret"
+    IPSEC_ID = "ipsecIdentifier"
+    IPSEC_SECRET = "ipsecSecret"
+    IPSEC_USER_CERT = "ipsecUserCert"
+    IPSEC_CA_CERT = "ipsecCaCert"
+    IPSEC_SERVER_CERT = "ipsecServerCert"
+
+# Enums for VPN profile types
+class VpnProfileType(enum.Enum):
+    """ Integer constant for each type of VPN
+    """
+    PPTP = 0
+    L2TP_IPSEC_PSK = 1
+    L2TP_IPSEC_RSA = 2
+    IPSEC_XAUTH_PSK = 3
+    IPSEC_XAUTH_RSA = 4
+    IPSEC_HYBRID_RSA = 5
+
+# Constants for config file
+class VpnReqParams(object):
+    """ Config file parameters required for
+        VPN connection
+    """
+    wifi_network = "wifi_network"
+    vpn_server_addresses = "vpn_server_addresses"
+    vpn_verify_address = "vpn_verify_address"
+    vpn_username = "vpn_username"
+    vpn_password = "vpn_password"
+    psk_secret = "psk_secret"
+    client_pkcs_file_name = "client_pkcs_file_name"
+    cert_path_vpnserver = "cert_path_vpnserver"
+    cert_password = "cert_password"
+    pptp_mppe = "pptp_mppe"
diff --git a/acts/tests/google/net/LegacyVpnTest.py b/acts/tests/google/net/LegacyVpnTest.py
new file mode 100644
index 0000000..a0d844f
--- /dev/null
+++ b/acts/tests/google/net/LegacyVpnTest.py
@@ -0,0 +1,185 @@
+#
+#   Copyright 2016 - The Android Open Source Project
+#
+#   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 logging
+import os
+import pprint
+import time
+import urllib.request
+
+from acts import asserts
+from acts import base_test
+from acts import test_runner
+from acts.controllers import adb
+from acts.test_utils.wifi import wifi_test_utils
+from acts.test_utils.net import connectivity_const
+
+VPN_CONST = connectivity_const.VpnProfile
+VPN_TYPE = connectivity_const.VpnProfileType
+VPN_PARAMS = connectivity_const.VpnReqParams
+
+
+class LegacyVpnTest(base_test.BaseTestClass):
+    """ Tests for Legacy VPN in Android
+
+        Testbed requirement:
+            1. One Android device
+            2. A Wi-Fi network that can reach the VPN servers
+    """
+
+    def setup_class(self):
+        """ Setup wi-fi connection and unpack params
+        """
+        self.dut = self.android_devices[0]
+        required_params = dir(VPN_PARAMS)
+        required_params = [x for x in required_params if not x.startswith('__')]
+        self.unpack_userparams(required_params)
+        wifi_test_utils.wifi_test_device_init(self.dut)
+        wifi_test_utils.wifi_connect(self.dut, self.wifi_network)
+
+    def teardown_class(self):
+        """ Reset wifi to make sure VPN tears down cleanly
+        """
+        wifi_test_utils.reset_wifi(self.dut)
+
+    def download_load_certs(self,vpn_type_name):
+        """ Download the certificates from VPN server and push to sdcard of DUT
+
+            Args:
+                VPN type name
+
+            Returns:
+                Client cert file name on DUT's sdcard
+        """
+        url = "http://%s%s%s" % (self.vpn_server_addresses[vpn_type_name],
+                                 self.cert_path_vpnserver,
+                                 self.client_pkcs_file_name)
+        local_cert_name = "%s_%s" % (vpn_type_name,self.client_pkcs_file_name)
+        local_file_path = os.path.join(self.log_path, local_cert_name)
+        try:
+            ret = urllib.request.urlopen(url)
+            with open(file_path, "wb") as f:
+                f.write(ret.read())
+        except:
+            asserts.fail("Unable to download certificate from the server")
+        f.close()
+        self.dut.adb.push("%s sdcard/" % local_file_path)
+        return local_cert_name
+
+    def generate_legacy_vpn_profile(self,vpn_type):
+        """ Generate legacy VPN profile for a VPN
+
+            Args:
+                VpnProfileType
+        """
+        vpn_profile = {VPN_CONST.USER: self.vpn_username,
+                       VPN_CONST.PWD: self.vpn_password,
+                       VPN_CONST.TYPE: vpn_type.value,
+                       VPN_CONST.SERVER: self.vpn_server_addresses[vpn_type.name],}
+        vpn_profile[VPN_CONST.NAME] = "test_%s" % vpn_type.name
+        psk_set = set(["L2TP_IPSEC_PSK", "IPSEC_XAUTH_PSK"])
+        rsa_set = set(["L2TP_IPSEC_RSA", "IPSEC_XAUTH_RSA", "IPSEC_HYBRID_RSA"])
+        if vpn_type.name in psk_set:
+            vpn_profile[VPN_CONST.IPSEC_SECRET] = self.psk_secret
+        elif vpn_type.name in rsa_set:
+            cert_name = self.download_load_certs(vpn_type.name)
+            vpn_profile[VPN_CONST.IPSEC_USER_CERT] = "%s_%s" % (vpn_type.name,
+                                                      cert_name.split('.')[0])
+            vpn_profile[VPN_CONST.IPSEC_CA_CERT] = "%s_%s" % (vpn_type.name,
+                                                   cert_name.split('.')[0])
+            self.dut.droid.installCertificate(vpn_profile,cert_name,self.cert_password)
+        else:
+            vpn_profile[VPN_CONST.MPPE] = self.pptp_mppe
+        return vpn_profile
+
+    def verify_ping_to_vpn_ip(self, connected_vpn_info):
+        """ Verify if IP behind VPN server is pingable
+            Ping should pass, if VPN is connected
+            Ping should fail, if VPN is disconnected
+
+            Args:
+                connected_vpn_info which specifies the VPN connection status
+        """
+        try:
+            ping_result = self.dut.adb.shell("ping -c 3 -W 2 %s"
+                                             % self.vpn_verify_address)
+            if not connected_vpn_info and "100% packet loss" \
+                not in "%s" % ping_result:
+                  asserts.fail("VPN is disconnected.\
+                               Ping to the internal IP expected to fail")
+        except adb.AdbError as ping_error:
+            ping_error = "%s" % ping_error
+            if connected_vpn_info and "100% packet loss" in ping_error:
+                asserts.fail("Ping to the internal IP failed.\
+                             Expected to pass as VPN is connected")
+
+    def legacy_vpn_connection_test_logic(self, vpn_type):
+        """ Test logic for each legacy VPN connection
+
+            Steps:
+                1. Generate profile for the VPN type
+                2. Establish connection to the server
+                3. Verify that connection is established using LegacyVpnInfo
+                4. Verify the connection by pinging the IP behind VPN
+                5. Stop the VPN connection
+                6. Check the connection status
+                7. Verify that ping to IP behind VPN fails
+
+            Args:
+                VpnProfileType (1 of the 6 types supported by Android)
+        """
+        vpn_profile = self.generate_legacy_vpn_profile(vpn_type)
+        logging.info("Connecting to: %s", vpn_profile)
+        self.dut.droid.vpnStartLegacyVpn(vpn_profile)
+        time.sleep(connectivity_const.VPN_TIMEOUT)
+        connected_vpn_info = self.dut.droid.vpnGetLegacyVpnInfo()
+        asserts.assert_equal(connected_vpn_info["state"],
+                             connectivity_const.VPN_STATE_CONNECTED,
+                             "Unable to establish VPN connection for %s"
+                             % vpn_profile)
+        self.verify_ping_to_vpn_ip(connected_vpn_info)
+        self.dut.droid.vpnStopLegacyVpn()
+        connected_vpn_info = self.dut.droid.vpnGetLegacyVpnInfo()
+        asserts.assert_true(not connected_vpn_info,
+                            "Unable to terminate VPN connection for %s"
+                            % vpn_profile)
+        self.verify_ping_to_vpn_ip(connected_vpn_info)
+
+    """ Test Cases """
+
+    def test_connection_to_legacy_vpn(self):
+        """ Verify VPN connection for all configurations.
+            Supported VPN configurations are
+            1.) PPTP            2.) L2TP IPSEC PSK
+            3.) IPSEC XAUTH PSK 4.) L2TP IPSEC RSA
+            5.) IPSEC XAUTH RSA 6.) IPSec Hybrid RSA
+
+            Steps:
+                1. Call legacy_vpn_connection_test_logic() for each VPN which
+                tests the connection to the corresponding server
+
+            Return:
+                Pass: if all VPNs pass
+                Fail: if any one VPN fails
+        """
+        def gen_name(vpn_type):
+            return "test_legacy_vpn_%s" % vpn_type.name
+
+        result = self.run_generated_testcases(self.legacy_vpn_connection_test_logic,
+                                              VPN_TYPE,
+                                              name_func=gen_name,)
+        msg = ("The following configs failed vpn connection %s"
+               % pprint.pformat(result))
+        asserts.assert_equal(len(result), 0, msg)