blob: ea37932c4de4cb21641cf3135e037e0cc493eee3 [file] [log] [blame]
# 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)
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
profie 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:
_, installed_profiles = \
request_installed_profiles(euicc_path, hermes_manager)
target_state = 'ACTIVE' if is_enable else 'INACTIVE'
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:
# getting a disabled profile on a prod esim, so that we can
# test enable next.
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)