| # Copyright 2021 The Chromium OS Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| import dbus |
| import logging |
| import random |
| |
| from autotest_lib.client.common_lib import error |
| from autotest_lib.client.cros.cellular import hermes_constants |
| from autotest_lib.client.cros.networking import hermes_proxy |
| from autotest_lib.client.cros.networking import mm1_proxy |
| |
| # Helper functions |
| def connect_to_hermes(): |
| """ |
| Attempts to connect to a DBus object. |
| |
| @raise: error.TestFail if connection fails. |
| |
| """ |
| hermes_manager = 'None' |
| try: |
| hermes_manager = \ |
| hermes_proxy.HermesManagerProxy().get_hermes_manager() |
| except dbus.DBusException as e: |
| logging.error('get_hermes_manager error:%s', e) |
| raise error.TestFail('Connect to Hermes failed') |
| if hermes_manager is 'None': |
| raise error.TestFail('Could not get connection to Hermes') |
| return hermes_manager |
| |
| def request_installed_profiles(euicc_path, hermes_manager): |
| """ |
| Check euicc at given path |
| |
| @param euicc_path: path of the sim given |
| @return a dict of profiles objects. Returns None if no profile is found |
| @raise: error.TestFail if no euicc matching given path |
| |
| """ |
| euicc = hermes_manager.get_euicc(euicc_path) |
| if not euicc: |
| raise error.TestFail('No euicc found at:', euicc_path) |
| |
| euicc.request_installed_profiles() |
| installed_profiles = euicc.get_installed_profiles() |
| if not installed_profiles: |
| logging.info('No installed profiles on euicc:%s', euicc_path) |
| return euicc, installed_profiles |
| |
| def mm_inhibit(is_inhibit, mm_proxy): |
| """ |
| Suspend/Resume modemmanager DBus daemon |
| |
| @param is_inhibit: true if to suspend MM, false to reconnect MM |
| @device: modem 'Device' value obtained or None, sysfs path of the device |
| Ex:/virtual/fake for trogdor |
| @raise error.TestFail if any dbus exception happens |
| |
| """ |
| try: |
| logging.info('Modem Manager Inhibit/UnInhibit start') |
| mm_proxy.inhibit_device(dbus.Boolean(is_inhibit)) |
| except dbus.exceptions.DBusException as error: |
| raise error.TestFail('mm_inhibit failed. error:', error) |
| |
| def install_profile(euicc_path, hermes_manager, is_prod_ci): |
| """ |
| Install a profile on the euicc at euicc_path. |
| |
| @param euicc_path: esim path based on testci/prodci |
| @param hermes_manager: hermes manager object |
| @param is_prod_ci: true if it is prodci test and false for testci |
| @return iccid: iccid of the installed profile or None |
| |
| """ |
| if not is_prod_ci: |
| is_smds_test = random.choice([True, False]) |
| logging.info('is_smds_test %s', is_smds_test) |
| if is_smds_test: |
| installed_iccid = install_pending_profile_test( |
| euicc_path, hermes_manager) |
| else: |
| installed_iccid = install_profile_test( |
| euicc_path, hermes_manager) |
| else: |
| installed_iccid = get_profile( |
| euicc_path, hermes_manager, False) |
| return installed_iccid |
| |
| def uninstall_all_profiles(euicc_path, hermes_manager): |
| """ |
| Uninstalls all installed test profiles |
| |
| @param euicc_path: esim path based on testci/prodci |
| @param hermes_manager: hermes manager object |
| @raise error.TestFail if any dbus exception happens |
| |
| """ |
| try: |
| euicc, installed_profiles = \ |
| request_installed_profiles(euicc_path, hermes_manager) |
| |
| profiles_count = len(installed_profiles) |
| if profiles_count is 0: return |
| |
| # also skips iccid 89010000001234567882 - R&S as its TESTING profile |
| for profile in installed_profiles.keys(): |
| if ((hermes_constants.ProfileStateToString( |
| installed_profiles[profile].state) == 'INACTIVE') and |
| (hermes_constants.ProfileClassToString( |
| installed_profiles[profile].profileclass) != |
| 'TESTING')): |
| |
| logging.info('Uninstalling profile - iccid:%s', |
| installed_profiles[profile].iccid) |
| euicc.uninstall_profile(profile) |
| logging.info('Uninstall done') |
| except dbus.DBusException as e: |
| logging.error('Failed to uninstall a profile error:%s', e) |
| raise error.TestFail('Failed to uninstall profile') |
| |
| def initialize_test(is_prod_ci): |
| """ |
| Initialize euicc paths, connect to hermes, set test mode |
| |
| @param is_prod_ci: true if it is prodci test and false for testci |
| |
| """ |
| logging.info('===initialize_test started===') |
| mm_proxy = mm1_proxy.ModemManager1Proxy.get_proxy() |
| # Do MM inhibit. uninhibit happens automatically after the test exit. |
| mm_inhibit(True, mm_proxy) |
| |
| logging.info('Connect to Hermes') |
| hermes_manager = connect_to_hermes() |
| |
| # Always euicc/0 is prod one and euicc/1 is for test esim profiles |
| # we prefer to hardcode euicc/0, since it acts as a check that Hermes |
| # is able to initialize without any error. If hermes encounters an |
| # error, hermes will start exposing objects such as |
| # self.prod_euicc_path = "/org/chromium/Hermes/euicc/22" |
| # self.test_euicc_path = "/org/chromium/Hermes/euicc/23" |
| |
| prod_euicc_path = "/org/chromium/Hermes/euicc/0" |
| test_euicc_path = "/org/chromium/Hermes/euicc/1" |
| euicc_path = prod_euicc_path if is_prod_ci \ |
| else test_euicc_path |
| |
| euicc = hermes_manager.get_euicc(euicc_path) |
| if not euicc: |
| raise error.TestFail("Initialize test failed, euicc not found") |
| euicc.use_test_certs(not is_prod_ci) |
| |
| if not is_prod_ci: |
| uninstall_all_profiles(euicc_path, hermes_manager) |
| logging.info('===initialize_test done===\n') |
| return mm_proxy, hermes_manager, euicc_path |
| |
| def validate_profile_state(euicc_path, hermes_manager, iccid, is_enable): |
| """ |
| Validates given profile(iccid) state and all installed profile states |
| |
| One profile enabled should result other profiles to be disabled and if any |
| profile disabled then should see all the profiles in disabled state |
| |
| @param euicc_path: esim path based on testci/prodci |
| @param hermes_manager: hermes manager object |
| @param iccid: iccid of the profile enabled/disabled |
| @param is_enable: true to enable profile and false to disable |
| @raise error.TestFail if any dbus exception happens |
| |
| """ |
| try: |
| target_state = 'ACTIVE' if is_enable else 'INACTIVE' |
| _, installed_profiles = \ |
| request_installed_profiles(euicc_path, hermes_manager) |
| is_equal = False |
| # check profile with given iccid has target_state and rest are opposite |
| for profile in installed_profiles.values(): |
| if (hermes_constants.ProfileStateToString(profile.state) == |
| target_state): |
| is_equal = True |
| else: |
| is_equal = False |
| # check for active profiles when disabled |
| if not is_enable and not is_equal: |
| logging.error('profile:%s not in %s state', |
| profile.iccid, target_state) |
| raise error.TestFail('validate_profile_state failed') |
| else: |
| continue |
| # check for inactive profiles when enabled except enabled one |
| if iccid == profile.iccid: |
| if not is_equal: |
| logging.error('profile:%s not in %s state', |
| profile.iccid, target_state) |
| raise error.TestFail('validate_profile_state failed') |
| else: |
| if is_equal and is_enable: |
| logging.error('profile:%s in %s state not valid', |
| profile.iccid, target_state) |
| raise error.TestFail('validate_profile_state failed') |
| logging.info('validate_profile_state succeeded') |
| except dbus.DBusException as e: |
| logging.error('Profile %s error:%s', target_state, e) |
| raise error.TestFail('validate_profile_state failed') |
| |
| def set_profile_state( |
| is_active, euicc_path=None, hermes_manager=None, iccid=None, profile=None): |
| """ |
| Enable or Disable already enabled/disabled profile |
| |
| @param is_active: True to enable, False to disable profile |
| @param euicc_path: esim path based on testci/prodci |
| @param hermes_manager: hermes manager object |
| @param iccid: profile iccid to enable |
| @param profile: profile object to enable/disable |
| @raise error.TestFail if expected error not resulted |
| |
| """ |
| logging.info('set_profile_state start') |
| if euicc_path and iccid: |
| euicc = hermes_manager.get_euicc(euicc_path) |
| profile = euicc.get_profile_from_iccid(iccid) |
| |
| if is_active: |
| profile.enable() |
| else: |
| profile.disable() |
| logging.info('set_profile_state done') |
| |
| def get_profile(euicc_path, hermes_manager, is_active): |
| """ |
| Returns a active/inactive profile on given euicc |
| |
| This is to get already enabled or disabled profile. if not found enabled |
| profile, enable an inactive profile and if not found disable profile |
| disable an active profile |
| |
| @param euicc_path: esim path based on testci/prodci |
| @param hermes_manager: hermes manager object |
| @param is_active: True to get active profile, False to get inactive profile |
| @return iccid: iccid of the active/inactive profile as requested |
| @raise error.TestFail if any dbus exception happens |
| |
| """ |
| try: |
| _, installed_profiles = \ |
| request_installed_profiles(euicc_path, hermes_manager) |
| |
| profile_found = False |
| iccid = None |
| profile_needed = 'Enabled' if is_active else 'Disabled' |
| # Find active/inactive profile |
| target_state = 'ACTIVE' if is_active else 'INACTIVE' |
| |
| for profile in installed_profiles.values(): |
| # skipping TESTING profiles to prevent install/uninstall operations |
| if (hermes_constants.ProfileClassToString( |
| profile.profileclass) == 'TESTING'): |
| continue |
| |
| if not (hermes_constants.ProfileStateToString(profile.state) == |
| target_state): |
| set_profile_state(is_active, profile=profile) |
| |
| profile_found = True |
| return profile.iccid |
| |
| if not profile_found: |
| logging.error('No installed profile which is %s', profile_needed) |
| return iccid |
| except dbus.DBusException as e: |
| raise error.TestFail('get_profile failed :', repr(e)) |
| |
| def get_iccid_of_disabled_profile(euicc_path, hermes_manager, is_prod_ci): |
| """ |
| Get profile with disabled status and return its iccid |
| |
| For test esim install new profile and return iccid of that profile |
| For prod esim having two profiles is prerequisite, return disabled profile |
| |
| @param euicc_path: esim path based on testci/prodci |
| @param hermes_manager: hermes manager object |
| @param is_prod_ci: true if it is prodci test and false for testci |
| @return iccid: iccid of the installed profile or None |
| |
| """ |
| if not is_prod_ci: |
| installed_iccid = install_profile_test(euicc_path, hermes_manager) |
| else: |
| # get disabled profile on a prod esim, if not exist then do disable one |
| _, installed_profiles = \ |
| request_installed_profiles(euicc_path, hermes_manager) |
| for profile in installed_profiles.values(): |
| if (hermes_constants.ProfileClassToString(profile.profileclass) == |
| 'TESTING'): |
| continue |
| |
| if (hermes_constants.ProfileStateToString(profile.state) == |
| 'INACTIVE'): |
| return profile.iccid |
| |
| installed_iccid = get_profile(euicc_path, hermes_manager, False) |
| |
| return installed_iccid |
| |
| # Test functions |
| def enable_or_disable_profile_test( |
| euicc_path, hermes_manager, iccid, is_enable): |
| """ |
| Validates enable/disable profile api DBus call |
| |
| @param euicc_path: esim path based on testci/prodci |
| @param hermes_manager: hermes manager object |
| @param iccid: iccid of the profile to be enabled/disabled |
| @param is_enable: true to enable profile and false to disable |
| @raise error.TestFail if any dbus exception happens |
| |
| """ |
| try: |
| logging.info('===enable_or_disable_profile_test started===') |
| profile_action = 'Enable' if is_enable else 'Disable' |
| logging.info('%s :', profile_action) |
| euicc, installed_profiles = \ |
| request_installed_profiles(euicc_path, hermes_manager) |
| # Profile objects maybe stale if IsActive is false |
| # Switch to the euicc we are interested in before |
| # performing an op. |
| |
| profile_found = False |
| target_state = 'ACTIVE' if is_enable else 'INACTIVE' |
| # Find active or inactive profile to enable/disable |
| for profile in installed_profiles.values(): |
| if not (hermes_constants.ProfileStateToString(profile.state) == |
| target_state): |
| if iccid is None or iccid == profile.iccid: |
| logging.info('Profile to %s:%s', profile_action, |
| profile.iccid) |
| profile_found = True |
| set_profile_state(is_enable, profile=profile) |
| logging.info('===enable_or_disable_profile_test ' |
| 'succeeded===\n') |
| break |
| if not profile_found: |
| raise error.TestFail('enable_or_disable_profile_test failed -' |
| 'No profile to ' + profile_action) |
| # Check all profiles state |
| validate_profile_state(euicc_path, hermes_manager, iccid, is_enable) |
| except dbus.DBusException as e: |
| logging.error('Profile %s error:%s', profile_action, e) |
| raise error.TestFail('enable_or_disable_profile_test Failed') |
| |
| def install_profile_test(euicc_path, hermes_manager): |
| """ |
| Validates InstallProfileFromActivationCode api on test euicc |
| |
| use SMDS calls to find iccid, activation code from pending profiles |
| and install those profiles, this requires profiles generated based |
| on EID of test esims in lab devices |
| |
| @param euicc_path: esim path based on testci/prodci |
| @param hermes_manager: hermes manager object |
| @return iccid: iccid of the installed profile or None |
| @raise error.TestFail if any dbus exception happens |
| |
| """ |
| try: |
| # get all pending profiles which are generated on DUT EID |
| # Read all profiles activation code from pending profile dict |
| # Install a profile from activation code, have iccid and |
| # Check the presence of this profile after installation |
| |
| logging.info('===install_profile_test started===') |
| activation_code = None |
| confirmation_code = "" |
| iccid = None |
| euicc = None |
| |
| euicc, installed_profiles = \ |
| request_installed_profiles(euicc_path, hermes_manager) |
| |
| euicc.request_pending_profiles(dbus.String('prod.smds.rsp.goog')) |
| logging.info('euicc chosen:%s', euicc_path) |
| profiles_pending = euicc.get_pending_profiles() |
| if not profiles_pending: |
| logging.error('install_profile_test: pending profile not found') |
| raise error.TestFail('No pending profile found on euicc:', |
| euicc_path) |
| |
| profile_path_to_install, profile_to_install = \ |
| profiles_pending.items()[0] |
| logging.debug('First pending profile:%s', profile_path_to_install) |
| |
| iccid = profile_to_install.iccid |
| activation_code = profile_to_install.activationcode |
| |
| logging.info('Installing iccid:%s act_code:%s conf_code:%s', |
| iccid, activation_code, confirmation_code) |
| # Install |
| euicc.install_profile_from_activation_code( |
| activation_code, confirmation_code) |
| |
| # Check if iccid found in installed profiles, installation success |
| installed_profiles = euicc.get_installed_profiles() |
| |
| if ((installed_profiles[profile_path_to_install] is None) or |
| (installed_profiles[profile_path_to_install].iccid != |
| profile_to_install.iccid)): |
| logging.error('install_profile_test failed. Test Failed.') |
| raise error.TestFail('No installed profile found on euicc:', |
| euicc_path) |
| |
| logging.info('===install_profile_test succeeded===\n') |
| return iccid |
| except dbus.DBusException as e: |
| logging.error('Failed to install a pending profile') |
| raise error.TestFail('install_profile_test failed with ', |
| repr(e)) |
| |
| def install_pending_profile_test(euicc_path, hermes_manager): |
| """ |
| Validates InstallPendingProfile api on test euicc |
| Find a profile from list of esim pending profiles which is not |
| installed yet and install that profile |
| |
| Required to create pending profiles for each EID(euicc sim) in lab dut. |
| create profiles from stork giving lab devices EID. puts profiles in |
| pending state for that euicc when RequestPendingProfiles called. |
| |
| @param euicc_path: esim path based on testci/prodci |
| @param hermes_manager: hermes manager object |
| @return iccid: iccid of the installed profile or None |
| @raise error.TestFail if any dbus exception happens |
| |
| """ |
| logging.info('===install_pending_profile_test started===') |
| profile_to_install = None |
| |
| euicc, installed_profiles = \ |
| request_installed_profiles(euicc_path, hermes_manager) |
| |
| euicc.request_pending_profiles(dbus.String('prod.smds.rsp.goog')) |
| profiles_pending = euicc.get_pending_profiles() |
| if not profiles_pending: |
| logging.error( |
| 'install_pending_profile_test: pending profile not found') |
| raise error.TestFail('No pending profile found on euicc:', |
| euicc_path) |
| |
| profile_path_to_install, profile_to_install = profiles_pending.items()[0] |
| iccid = profile_to_install.iccid |
| activation_code = profile_to_install.activationcode |
| |
| logging.info('Installing profile:%s iccid:%s act_code:%s', |
| profile_path_to_install, iccid, activation_code) |
| |
| try: |
| # Install |
| profile = euicc.install_pending_profile( |
| profile_path_to_install, "") |
| logging.info('Installed pending profile is %s', profile) |
| if not profile: |
| logging.error('No profile object returned after install') |
| return None |
| except dbus.DBusException as e: |
| logging.error('Failed to install pending profile:%s', e) |
| raise error.TestFail('Failed to install pending profile', |
| repr(e)) |
| |
| # Find above installed profile, if not exists raise test failure |
| installed_profiles = euicc.get_installed_profiles() |
| if ((installed_profiles[profile_path_to_install] is None) or |
| (installed_profiles[profile_path_to_install].iccid != |
| profile_to_install.iccid)): |
| raise error.TestFail('Install pending profile failed :', |
| profile_path_to_install) |
| |
| logging.info('===install_pending_profile_test succeeded===\n') |
| return iccid |
| |
| def uninstall_profile_test(euicc_path, hermes_manager, iccid): |
| """ |
| Validates UninstallProfile api by uninstalling any randomly |
| selected installed profile |
| |
| @param euicc_path: esim path based on testci/prodci |
| @param hermes_manager: hermes manager object |
| @param iccid: iccid of the profile to be uninstalled |
| @raise error.TestFail if any dbus exception happens |
| |
| """ |
| logging.info('===uninstall_profile_test started===') |
| # Getinstalled profiles list and randomly uninstall a profile |
| try: |
| euicc, installed_profiles = \ |
| request_installed_profiles(euicc_path, hermes_manager) |
| |
| profile_to_uninstall = euicc.get_profile_from_iccid(iccid) |
| if not profile_to_uninstall: |
| raise error.TestFail('No valid profile found at:', euicc_path) |
| |
| profile_path = profile_to_uninstall.path |
| uninstalled_profile = None |
| |
| # Hermes does not support uninstalling test profiles yet. |
| if hermes_constants.ProfileClassToString( |
| profile_to_uninstall.profileclass) != 'TESTING': |
| logging.info('profile to uninstall is:%s', profile_path) |
| euicc.uninstall_profile(profile_path) |
| uninstalled_profile = profile_path |
| logging.info('uninstall_profile_test succeeded') |
| |
| if not uninstalled_profile: |
| raise error.TestFail( |
| 'uninstall_profile_test failed - No uninstallable profile') |
| |
| # Try to find the uninstalled profile, if exists raise test failure |
| profiles_installed = euicc.get_installed_profiles() |
| for profile in profiles_installed.keys(): |
| if uninstalled_profile in profile: |
| raise error.TestFail('uninstall_profile_test profile Failed') |
| logging.info('===uninstall_profile_test succeeded===\n') |
| except dbus.DBusException as e: |
| raise error.TestFail('Failed to uninstall profile', e) |