<commit_before># Copyright 2015 Canonical, Ltd.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

import datetime
import logging
import os
import subprocess
import time
import yaml
import subiquitycore.utils as utils


log = logging.getLogger("subiquitycore.curtin")

TMPDIR = '/tmp'
CURTIN_SEARCH_PATH = ['/usr/local/curtin/bin', '/usr/bin']
CURTIN_INSTALL_PATH = ['/media/root-ro', '/']
CURTIN_INSTALL_LOG = '/tmp/subiquity-curtin-install.log'
CURTIN_POSTINSTALL_LOG = '/tmp/subiquity-curtin-postinstall.log'
CONF_PREFIX = os.path.join(TMPDIR, 'subiquity-config-')
CURTIN_NETWORK_CONFIG_FILE = CONF_PREFIX + 'network.yaml'
CURTIN_STORAGE_CONFIG_FILE = CONF_PREFIX + 'storage.yaml'
CURTIN_PRESERVED_CONFIG_FILE = CONF_PREFIX + 'storage-preserved.yaml'
POST_INSTALL_CONFIG_FILE = CONF_PREFIX + 'postinst.yaml'
CURTIN_CONFIGS = {
    'network': CURTIN_NETWORK_CONFIG_FILE,
    'storage': CURTIN_STORAGE_CONFIG_FILE,
    'postinstall': POST_INSTALL_CONFIG_FILE,
    'preserved': CURTIN_PRESERVED_CONFIG_FILE,
}
CURTIN_CONFIG_HEADER = """
reporting:
 subiquity:
  type: print

partitioning_commands:
 builtin: curtin block-meta custom

"""

CURTIN_LOG_HEADER = """
install:
  log_file: {}
"""

CURTIN_CONFIG_REBOOT = """
power_state:
  message: s-Ubiquity install complete. Rebooting
  mode: reboot
"""
CURTIN_STORAGE_CONFIG_HEADER = """
storage:
  version: 1
  config:
"""
CURTIN_NETWORK_CONFIG_HEADER = """
network:
  version: 1
  config:
"""

# TODO, this should be moved to the in-target cloud-config seed so on first
# boot of the target, it reconfigures datasource_list to none for subsequent
# boots.
# Reworked for flake8, but it does make it harder to read.
POST_INSTALL_LIST = [
    ("late_commands:"),
    ("    10_mkdir_seed: curtin in-target -- "
     "mkdir -p /var/lib/cloud/seed/nocloud-net"),
    ("    11_postinst_metadata: [curtin, in-target, --, sh, '-c',"
     '"/bin/echo -e instance-id: inst-3011 '
     '> /var/lib/cloud/seed/nocloud-net/meta-data"]'),
    ("    12_postinst_userdata: [curtin, in-target, --, sh, '-c',"
     "\"/bin/echo -e '#cloud-config\\npassword: <PASSWORD>\\nchpasswd: "
     "{{ expire: False }}\\n{hostinfo}\\nusers:\\n{users}' > "
     "/var/lib/cloud/seed/nocloud-net/user-data\"]"),
]
POST_INSTALL = '\n' + "\n".join(POST_INSTALL_LIST) + '\n'


def curtin_configure_user(userinfo, dryrun=False):
    usercmds = []
    usercmds += ["useradd -m -p {confirm_password} {username}".format(**userinfo)]
    if 'ssh_import_id' in userinfo:
        target = "/home/{username}/.ssh/authorized_keys".format(**userinfo)
        userinfo.update({'target': target})
        ssh_id = userinfo.get('ssh_import_id')
        if ssh_id.startswith('sso'):
            log.info('call out to SSO login')
        else:
            ssh_import_id = "ssh-import-id -o "
            ssh_import_id += "{target} {ssh_import_id}".format(**userinfo)
            usercmds += [ssh_import_id]

    if not dryrun:
        for cmd in usercmds:
            utils.run_command(cmd.split(), shell=False)

        # always run chown last
        homedir = '/home/{username}'.format(**userinfo)
        retries = 10
        while not os.path.exists(homedir) and retries > 0:
            log.debug('waiting on homedir')
            retries -= 1
            time.sleep(0.2)

        if retries <= 0:
            raise ValueError('Failed to create homedir')
        
        chown = "chown {username}.{username} -R /home/{username}".format(**userinfo)
        utils.run_command(chown.split(), shell=False)

        # add sudo rule
        with open('/etc/sudoers.d/firstboot-user', 'w') as fh:<commit_msg>XXX: adduser with extrausers database<commit_after># Copyright 2015 Canonical, Ltd.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

import datetime
import logging
import os
import subprocess
import time
import yaml
import subiquitycore.utils as utils


log = logging.getLogger("subiquitycore.curtin")

TMPDIR = '/tmp'
CURTIN_SEARCH_PATH = ['/usr/local/curtin/bin', '/usr/bin']
CURTIN_INSTALL_PATH = ['/media/root-ro', '/']
CURTIN_INSTALL_LOG = '/tmp/subiquity-curtin-install.log'
CURTIN_POSTINSTALL_LOG = '/tmp/subiquity-curtin-postinstall.log'
CONF_PREFIX = os.path.join(TMPDIR, 'subiquity-config-')
CURTIN_NETWORK_CONFIG_FILE = CONF_PREFIX + 'network.yaml'
CURTIN_STORAGE_CONFIG_FILE = CONF_PREFIX + 'storage.yaml'
CURTIN_PRESERVED_CONFIG_FILE = CONF_PREFIX + 'storage-preserved.yaml'
POST_INSTALL_CONFIG_FILE = CONF_PREFIX + 'postinst.yaml'
CURTIN_CONFIGS = {
    'network': CURTIN_NETWORK_CONFIG_FILE,
    'storage': CURTIN_STORAGE_CONFIG_FILE,
    'postinstall': POST_INSTALL_CONFIG_FILE,
    'preserved': CURTIN_PRESERVED_CONFIG_FILE,
}
CURTIN_CONFIG_HEADER = """
reporting:
 subiquity:
  type: print

partitioning_commands:
 builtin: curtin block-meta custom

"""

CURTIN_LOG_HEADER = """
install:
  log_file: {}
"""

CURTIN_CONFIG_REBOOT = """
power_state:
  message: s-Ubiquity install complete. Rebooting
  mode: reboot
"""
CURTIN_STORAGE_CONFIG_HEADER = """
storage:
  version: 1
  config:
"""
CURTIN_NETWORK_CONFIG_HEADER = """
network:
  version: 1
  config:
"""

# TODO, this should be moved to the in-target cloud-config seed so on first
# boot of the target, it reconfigures datasource_list to none for subsequent
# boots.
# Reworked for flake8, but it does make it harder to read.
POST_INSTALL_LIST = [
    ("late_commands:"),
    ("    10_mkdir_seed: curtin in-target -- "
     "mkdir -p /var/lib/cloud/seed/nocloud-net"),
    ("    11_postinst_metadata: [curtin, in-target, --, sh, '-c',"
     '"/bin/echo -e instance-id: inst-3011 '
     '> /var/lib/cloud/seed/nocloud-net/meta-data"]'),
    ("    12_postinst_userdata: [curtin, in-target, --, sh, '-c',"
     "\"/bin/echo -e '#cloud-config\\npassword: passw0rd\\nchpasswd: "
     "{{ expire: False }}\\n{hostinfo}\\nusers:\\n{users}' > "
     "/var/lib/cloud/seed/nocloud-net/user-data\"]"),
]
POST_INSTALL = '\n' + "\n".join(POST_INSTALL_LIST) + '\n'


def curtin_configure_user(userinfo, dryrun=False):
    usercmds = []
    usercmds += ["useradd --extrausers -m -p {confirm_password} {username}".format(**userinfo)]
    if 'ssh_import_id' in userinfo:
        target = "/home/{username}/.ssh/authorized_keys".format(**userinfo)
        userinfo.update({'target': target})
        ssh_id = userinfo.get('ssh_import_id')
        if ssh_id.startswith('sso'):
            log.info('call out to SSO login')
        else:
            ssh_import_id = "ssh-import-id -o "
            ssh_import_id += "{target} {ssh_import_id}".format(**userinfo)
            usercmds += [ssh_import_id]

    if not dryrun:
        for cmd in usercmds:
            utils.run_command(cmd.split(), shell=False)

        # always run chown last
        homedir = '/home/{username}'.format(**userinfo)
        retries = 10
        while not os.path.exists(homedir) and retries > 0:
            log.debug('waiting on homedir')
            retries -= 1
            time.sleep(0.2)

        if retries <= 0:
            raise ValueError('Failed to create homedir')
        
        chown = "chown {username}.{username} -R /home/{username}".format(**userinfo)
        utils.run_command(chown.split(), shell=False)

        # add sudo rule
        with open('/etc/sudoers.d/firstboot-user', 'w') as fh:
{'fraction_non_alphanumeric': 0.12858660998937302, 'fraction_numerical': 0.0060219624512929506, 'mean_word_length': 3.4438614900314795, 'pattern_counts': {'":': 0, '<': 8, '<?xml version=': 0, '>': 12, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5595486', 'n_tokens_mistral': 2942, 'n_tokens_neox': 2806, 'n_words': 706}