def test_mount_config_source(self, mock_Command_run):
        mount_result = Mock()
        mount_result_codes = [0, 1]

        def command_result(self, **args):
            mount_result.returncode = mount_result_codes.pop()
            return mount_result

        mock_Command_run.side_effect = command_result

        result = Defaults.mount_config_source()
        assert result.name == 'suse_firstboot_config.yaml'
        assert result.location == '/mnt'
        assert result.label == 'azconfig'

        assert mock_Command_run.call_args_list == [
            call(['mountpoint', '/mnt'], raise_on_error=False),
            call(['mount', '-o', 'sync', '--label', 'azconfig', '/mnt'],
                 raise_on_error=False)
        ]

        mount_result.returncode = 1
        mock_Command_run.side_effect = None
        mock_Command_run.reset_mock()

        with raises(AzureHostedConfigFileSourceMountException):
            Defaults.mount_config_source()

        assert mock_Command_run.call_args_list == [
            call(['mountpoint', '/mnt'], raise_on_error=False),
            call(['mount', '-o', 'sync', '--label', 'azconfig', '/mnt'],
                 raise_on_error=False),
            call(['mount', '/dev/dvd', '/mnt'], raise_on_error=False)
        ]
 def test_get_service_reports(self, mock_StatusReport):
     Defaults.get_service_reports()
     assert mock_StatusReport.call_args_list == [
         call('call',
              systemd_service_name='azure-li-call',
              init_state=False),
         call('config_lookup',
              systemd_service_name='azure-li-config-lookup',
              init_state=False),
         call('install',
              systemd_service_name='azure-li-install',
              init_state=False),
         call('machine_constraints',
              systemd_service_name='azure-li-machine-constraints',
              init_state=False),
         call('network',
              systemd_service_name='azure-li-network',
              init_state=False),
         call('storage',
              systemd_service_name='azure-li-storage',
              init_state=False),
         call('system_setup',
              systemd_service_name='azure-li-system-setup',
              init_state=False),
         call('user',
              systemd_service_name='azure-li-user',
              init_state=False)
     ]
Exemplo n.º 3
0
def enable_extra_kernel_modules():
    modules = Defaults.get_stonith_needed_modules()
    file_content = ''
    for module in modules:
        file_content = ''.join([file_content, os.linesep, module])
        Command.run(['modprobe', module])

    load_module_path = Defaults.get_extra_kernel_modules_file_name()
    _write_file(load_module_path, file_content)
Exemplo n.º 4
0
def main():
    """
    Azure Li/Vli cleanup

    Uninstall azure-li-services package and its dependencies
    and check for potential reboot request
    """
    Logger.setup()
    service_reports = Defaults.get_service_reports()

    reboot_system = False
    all_services_successful = True
    for report in service_reports:
        if not report.get_state():
            # in case a service has unknown or failed state we will
            # not consider to reboot the machine
            all_services_successful = False
            reboot_system = False
            break
        if report.get_reboot():
            reboot_system = True

    install_source = Defaults.mount_config_source()
    try:
        state_file = os.sep.join([
            install_source.location,
            'workload_success_is_{}'.format(all_services_successful).lower()
        ])
        with open(state_file, 'w'):
            pass
        if not all_services_successful:
            write_service_log(install_source)
    finally:
        Defaults.umount_config_source(install_source)

    Command.run([
        'zypper', '--non-interactive', 'remove', '--clean-deps',
        '--force-resolution', 'azure-li-services'
    ])
    Command.run(['systemctl', 'reset-failed'])

    if reboot_system:
        Command.run([
            'kexec', '--load', '/boot/vmlinuz', '--initrd', '/boot/initrd',
            '--command-line',
            get_boot_cmdline()
        ])
        Command.run(['kexec', '--exec'])
def main():
    """
    Azure Li/Vli machine constraints

    Validation of machine requirements in the scope of an Azure Li/Vli instance
    """
    Logger.setup()
    status = StatusReport('machine_constraints')
    config = RuntimeConfig(Defaults.get_config_file())
    machine_constraints = config.get_machine_constraints()

    constraint_errors = []

    if machine_constraints:
        try:
            check_cpu_count_validates_constraint(machine_constraints)
        except Exception as issue:
            constraint_errors.append(issue)
        try:
            check_main_memory_validates_constraint(machine_constraints)
        except Exception as issue:
            constraint_errors.append(issue)

    if constraint_errors:
        raise AzureHostedException(constraint_errors)

    status.set_success()
Exemplo n.º 6
0
def write_service_log(install_source):
    log_file = os.sep.join([install_source.location, 'workload.log'])
    bash_command = ' '.join(
        ['systemctl', 'status', '-l', '--all', '&>', log_file])
    Command.run(['bash', '-c', bash_command], raise_on_error=False)
    Command.run(['cp', Defaults.get_log_file(), install_source.location],
                raise_on_error=False)
Exemplo n.º 7
0
def main():
    """
    Azure Li/Vli script call

    Calls a custom script in the scope of an Azure Li/Vli instance
    """
    Logger.setup()
    status = StatusReport('call')
    config = RuntimeConfig(Defaults.get_config_file())
    call_script = config.get_call_script()

    if call_script:
        call_source = Defaults.mount_config_source()
        Command.run([
            'bash', '-c', '{0}/{1}'.format(call_source.location, call_script)
        ])

    status.set_success()
Exemplo n.º 8
0
def main():
    """
    Azure Li/Vli storage mount setup

    Updates fstab with new storage mount entries and activates
    them in the scope of an Azure Li/Vli instance
    """
    Logger.setup()
    status = StatusReport('storage')
    config = RuntimeConfig(Defaults.get_config_file())
    storage_config = config.get_storage_config()

    storage_errors = []

    if storage_config:
        fstab_entries = []
        for storage in storage_config:
            try:
                if 'device' not in storage or 'mount' not in storage:
                    raise AzureHostedStorageMountException(
                        'At least one of {0} missing in {1}'.format(
                            ('device', 'mount'), storage))
                Path.create(storage['mount'])
                fstab_entries.append(
                    '{device} {mount} {fstype} {options} 0 0'.format(
                        device=storage['device'],
                        mount=storage['mount'],
                        fstype=storage.get('file_system') or 'auto',
                        options=','.join(
                            storage.get('mount_options', ['defaults']))))
            except Exception as issue:
                storage_errors.append(issue)

        if fstab_entries:
            with open('/etc/fstab', 'a') as fstab:
                fstab.write(os.linesep)
                for entry in fstab_entries:
                    fstab.write(entry)
                    fstab.write(os.linesep)

            Command.run(['mount', '-a'])

            for storage in storage_config:
                min_size = storage.get('min_size')
                if min_size:
                    try:
                        check_storage_size_validates_constraint(
                            min_size, storage['mount'])
                    except Exception as issue:
                        storage_errors.append(issue)

    if storage_errors:
        raise AzureHostedException(storage_errors)

    status.set_success()
    def test_mount_config_source_already_mounted(self, mock_Command_run):
        mountpoint_result = Mock()
        mountpoint_result.returncode = 0
        mock_Command_run.return_value = mountpoint_result

        result = Defaults.mount_config_source()
        assert result.name == 'suse_firstboot_config.yaml'
        assert result.location == '/mnt'
        assert result.label == 'azconfig'

        mock_Command_run.assert_called_once_with(['mountpoint', '/mnt'],
                                                 raise_on_error=False)
Exemplo n.º 10
0
    def setup():
        logger = logging.getLogger('Azure_LI_Services')
        logger.setLevel(logging.INFO)

        log_file = logging.FileHandler(Defaults.get_log_file())
        log_file.setLevel(logging.INFO)

        log_stream = logging.StreamHandler()
        log_stream.setLevel(logging.INFO)

        logger.addHandler(log_stream)
        logger.addHandler(log_file)
Exemplo n.º 11
0
def main():
    """
    Azure Li/Vli config file lookup

    Lookup config file as provided by the Azure Li/VLi storage backend
    and make it locally available at the location described by
    Defaults.get_config_file_name()
    """
    Logger.setup()
    status = StatusReport('config_lookup')
    azure_config = Defaults.mount_config_source()

    try:
        azure_config_lookup_paths = [azure_config.location]
        azure_config_file = Path.which(
            azure_config.name, azure_config_lookup_paths
        )
        if not azure_config_file:
            raise AzureHostedConfigFileNotFoundException(
                'Config file not found at: {0}/{1}'.format(
                    azure_config.location, azure_config.name
                )
            )
        Path.create(
            os.path.dirname(Defaults.get_config_file_name())
        )
        Command.run(
            ['cp', azure_config_file, Defaults.get_config_file_name()]
        )
        os.chmod(Defaults.get_config_file_name(), 0o600)
        status.set_success()
    finally:
        Defaults.umount_config_source(azure_config)
Exemplo n.º 12
0
def main():
    """
    Azure Li/Vli package installation

    Creates a local rpm-md repository and registers it with zypper.
    Installs all packages configured in the scope of an Azure
    Li/Vli instance
    """
    Logger.setup()
    status = StatusReport('install')
    config = RuntimeConfig(Defaults.get_config_file())
    packages_config = config.get_packages_config()

    if packages_config:
        install_source = Defaults.mount_config_source()

        local_repos = {}
        local_repos.update(import_raw_sources(packages_config, install_source))
        local_repos.update(
            import_repository_sources(packages_config, install_source))
        for repository_name, repository_metadata in local_repos.items():
            repository_location = repository_metadata[0]
            Command.run(['zypper', 'removerepo', repository_name],
                        raise_on_error=False)
            Command.run([
                'zypper', 'addrepo', '--no-gpgcheck', repository_location,
                repository_name
            ])

        packages_to_install = []
        for repository_metadata in local_repos.values():
            packages_to_install += repository_metadata[1]
        if packages_to_install:
            Command.run([
                'zypper', '--non-interactive', 'install',
                '--auto-agree-with-licenses'
            ] + list(filter(None, packages_to_install)))

    status.set_success()
Exemplo n.º 13
0
def main():
    """
    Azure Li/Vli system setup

    Runs machine setup tasks in the scope of an Azure Li/Vli instance
    """
    Logger.setup()
    status = StatusReport('system_setup')
    config = RuntimeConfig(Defaults.get_config_file())
    hostname = config.get_hostname()
    stonith_config = config.get_stonith_config()

    system_setup_errors = []

    if hostname:
        try:
            set_hostname(hostname)
        except Exception as issue:
            system_setup_errors.append(issue)

    if stonith_config:
        try:
            set_stonith_service(stonith_config)
            enable_extra_kernel_modules()
        except Exception as issue:
            system_setup_errors.append(issue)

    try:
        set_kdump_service(config.get_crash_dump_config(), status)
    except Exception as issue:
        system_setup_errors.append(issue)

    try:
        set_kernel_samepage_merging_mode()
    except Exception as issue:
        system_setup_errors.append(issue)

    try:
        set_energy_performance_settings()
    except Exception as issue:
        system_setup_errors.append(issue)

    try:
        set_saptune_service()
    except Exception as issue:
        system_setup_errors.append(issue)

    if system_setup_errors:
        raise AzureHostedException(system_setup_errors)

    status.set_success()
Exemplo n.º 14
0
    def __init__(self,
                 service_name,
                 init_state=True,
                 systemd_service_name=None):
        self.systemd_service_name = systemd_service_name
        self.status = {service_name: {'success': None, 'reboot': False}}
        self.service_name = service_name
        self.status_directory = Defaults.get_status_report_directory()
        self.status_file = os.sep.join(
            [self.status_directory, self.service_name + '.report.yaml'])
        if not os.path.exists(self.status_directory):
            Path.create(self.status_directory)

        if init_state:
            self.set_failed()
Exemplo n.º 15
0
def main():
    """
    Azure Li/Vli status report

    Report overall service status in an update to /etc/issue
    if not all services were successful
    """
    Logger.setup()
    service_reports = Defaults.get_service_reports()

    failed_services = []
    for report in service_reports:
        success = report.get_state()
        if not success:
            failed_services.append(report.get_systemd_service())

    if failed_services:
        report_services_failed(failed_services)
Exemplo n.º 16
0
def main():
    """
    Azure Li/Vli user setup

    Creates the configured user and its access setup for ssh
    and sudo services in the scope of an Azure Li/Vli instance
    """
    Logger.setup()
    status = StatusReport('user')
    config = RuntimeConfig(Defaults.get_config_file())

    user_config = config.get_user_config()

    if not user_config:
        raise AzureHostedUserConfigDataException(
            'credentials section missing in config file')

    user_setup_errors = []

    for user in user_config:
        try:
            create_or_modify_user(user)
        except Exception as issue:
            user_setup_errors.append(issue)
        else:
            try:
                setup_ssh_authorization(user)
            except Exception as issue:
                user_setup_errors.append(issue)
            try:
                setup_sudo_authorization(user)
            except Exception as issue:
                user_setup_errors.append(issue)
            try:
                setup_user_attributes(user)
            except Exception as issue:
                user_setup_errors.append(issue)

    setup_sudo_config()

    if user_setup_errors:
        raise AzureHostedException(user_setup_errors)

    status.set_success()
Exemplo n.º 17
0
def setup_ssh_authorization(user):
    if 'ssh-key' in user or 'ssh-private-key' in user:
        if user['username'] == 'root':
            ssh_auth_dir = '/root/.ssh/'
            default_group = 'root'
        else:
            ssh_auth_dir = '/home/{0}/.ssh/'.format(user['username'])
            default_group = 'users'
        Path.create(ssh_auth_dir)
        group_setup = user['group'] if 'group' in user else {}
        uid = pwd.getpwnam(user['username']).pw_uid
        gid = grp.getgrnam(group_setup.get('name') or default_group).gr_gid
        os.chmod(ssh_auth_dir, 0o700)
        if user['username'] != 'root':
            os.chown(ssh_auth_dir, uid, gid)
        if 'ssh-key' in user:
            ssh_auth_file = ssh_auth_dir + 'authorized_keys'
            with open(ssh_auth_file, 'a') as ssh:
                ssh.write(os.linesep)
                ssh.write(user['ssh-key'])
            os.chmod(ssh_auth_file, 0o600)
            if user['username'] != 'root':
                os.chown(ssh_auth_file, uid, gid)
        if 'ssh-private-key' in user:
            ssh_key_source = Defaults.mount_config_source()
            private_key_file = user['ssh-private-key']
            Command.run([
                'cp',
                os.sep.join([ssh_key_source.location, private_key_file]),
                ssh_auth_dir
            ])
            ssh_key_file = os.path.normpath(
                os.sep.join([ssh_auth_dir,
                             os.path.basename(private_key_file)]))
            os.chmod(ssh_key_file, 0o600)
            if user['username'] != 'root':
                os.chown(ssh_key_file, uid, gid)
 def test_get_status_report_directory(self):
     assert Defaults.get_status_report_directory() == \
         '/var/lib/azure_li_services'
 def test_get_config_file(self, mock_os_path_exists):
     mock_os_path_exists.return_value = True
     assert Defaults.get_config_file() == '/etc/suse_firstboot_config.yaml'
     mock_os_path_exists.return_value = False
     with raises(AzureHostedConfigFileNotFoundException):
         Defaults.get_config_file()