<commit_before># Copyright 2014 Cloudbase Solutions Srl
# All Rights Reserved.
#
#    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.

"""Windows cloudbaseinit recipes."""

import ntpath
import os

import six

from argus import exceptions
from argus.introspection.cloud import windows as introspection
from argus.recipes.cloud import base
from argus import util
from argus.config_generator.windows import cb_init as cbinit_config

LOG = util.get_logger()

# Default values for an instance under booting step.
COUNT = 20
DELAY = 20
_CBINIT_REPO = "https://github.com/openstack/cloudbase-init"


class CloudbaseinitRecipe(base.BaseCloudbaseinitRecipe):
    """Recipe for preparing a Windows instance."""

    def wait_for_boot_completion(self):
        LOG.info("Waiting for first boot completion...")
        self._backend.remote_client.manager.wait_boot_completion()

    def set_mtu(self, interface="ipv4", subinterface_name="Ethernet",
                mtu_value=1400, store_type='active'):
        set_mtu_cmd = ('netsh interface {interface_type} set subinterface '
                       '"{name}" mtu={value} store={type}'
                       .format(interface_type=interface, value=mtu_value,
                               name=subinterface_name, type=store_type))
        LOG.info("Setting the MTU.")
        try:
            self._backend.remote_client.run_command_with_retry(set_mtu_cmd)
        except exceptions.ArgusTimeoutError as exc:
            LOG.debug('Setting MTU failed with %r.' % exc)

    def execution_prologue(self):
        LOG.info("Retrieve common module for proper script execution.")

        resource_location = "windows/common.psm1"
        self._backend.remote_client.manager.download_resource(
            resource_location=resource_location, location=r'C:\common.psm1')

    def get_installation_script(self):
        """Get instalation script for CloudbaseInit."""
        self._backend.remote_client.manager.get_installation_script()

    def install_cbinit(self):
        """Proceed on checking if cloudbase-init should be installed."""
        try:
            cbdir = introspection.get_cbinit_dir(self._execute)
        except exceptions.ArgusError:
            self._backend.remote_client.manager.install_cbinit()
            self._grab_cbinit_installation_log()
        else:
            # If the directory already exists, we won't be installing Cb-init.
            LOG.info("Cloudbase-init is already installed, skipping installation.")

    def _grab_cbinit_installation_log(self):
        """Obtain the installation logs."""
        LOG.info("Obtaining the installation logs.")
        if not self._conf.argus.output_directory:
            LOG.warning("The output directory wasn't given, "
                        "the log will not be grabbed.")
            return

        content = self._backend.remote_client.read_file("C:\\installation.log")
        log_template = "installation-{}.log".format(
            self._backend.instance_server()['id'])

        path = os.path.join(self._conf.argus.output_directory, log_template)
        with open(path, 'w') as stream:
            stream.write(content)

    def replace_install(self):
        """Replace the cb-init installed files with the downloaded ones.

        For the same file names, there will be a replace. The new ones
        will just be added and the other files will be left there.
        So it's more like an update.
        """
        link = self._conf.argus.patch_install
        if not link:
            return

        LOG.info("Replacing cloudbaseinit's files...")

        LOG.debug("Download and extract installation bundle.")
        if link.startswith("\\\\"):
            cmd = 'copy "{}" "C:\\install.zip"'.format(link)
            self._execute(cmd, command_type=util.CMD)
        else:
            location = r'C:\install.zip'
            self._backend.remote_client.manager.download(
                uri=link, location=location)
        cmds = [
            "Add-Type -A System.IO.Compression.FileSystem",
            "[IO.Compression.ZipFile]::ExtractToDirectory("
            "'C:\\install.zip', 'C:\\install')"
        ]
        cmd = '{}'.format("; ".join(cmds))
        self._execute(cmd, command_type=util.POWERSHELL)

        LOG.debug("Replace old files with the new ones.")
        cbdir = introspection.get_cbinit_dir(self._execute)
        self._execute('xcopy /y /e /q "C:\\install\\Cloudbase-Init"'
                      ' "{}"'.format(cbdir), command_type=util.CMD)

    def replace_code(self):
        """Replace the code of cloudbaseinit."""
        if not self._conf.argus.git_command:
            # Nothing to replace.
            return

        LOG.info("Replacing cloudbaseinit's code...")

        LOG.info("Getting cloudbase-init location...")
        # Get cb-init python location.
        python_dir = introspection.get_python_dir(self._execute)

        # Remove everything from the cloudbaseinit installation.
        LOG.info("Removing recursively cloudbaseinit...")
        cloudbaseinit = ntpath.join(
            python_dir,
            "Lib",
            "site-packages",
            "cloudbaseinit")
        self._execute('rmdir "{}" /S /q'.format(cloudbaseinit),
                      command_type=util.CMD)

        # Clone the repo
        LOG.info("Cloning the cloudbaseinit repo...")
        self._backend.remote_client.manager.git_clone(
            repo_url=_CBINIT_REPO,
            location=r"C:\cloudbaseinit")

        # Run the command provided at cli.
        LOG.info("Applying cli patch...")
        self._execute("cd C:\\cloudbaseinit && {}".format(
            self._conf.argus.git_command), command_type=util.CMD)

        # Replace the code, by moving the code from cloudbaseinit
        # to the installed location.
        LOG.info("Replacing code...")
        self._execute('Copy-Item C:\\cloudbaseinit\\cloudbaseinit '
                      '\'{}\' -Recurse'.format(cloudbaseinit),
                      command_type=util.POWERSHELL)

        # Autoinstall packages from the new requirements.txt
        python = ntpath.join(python_dir, "python.exe")
        command = '"{}" -m pip install -r C:\\cloudbaseinit\\requirements.txt'
        self._execute(command.format(python), command_type=util.CMD)

    def pre_sysprep(self):
        # Patch the installation of cloudbaseinit in order to create
        # a file when the execution ends. We're doing this instead of
        # monitoring the service, because on some OSes, just checking
        # if the service is stopped leads to errors, due to the
        # fact that the service starts later on.
        python_dir = introspection.get_python_dir(self._execute)
        cbinit = ntpath.join(python_dir, 'Lib', 'site-packages',
                             'cloudbaseinit')

        # Get the shell patching script and patch the installation.
        resource_location = "windows/patch_shell.ps1"
        params = r' "{}"'.format(cbinit)
        self._backend.remote_client.manager.execute_powershell_resource_script(
            resource_location=resource_location, parameters=params)

        # Prepare Something specific for the OS
        self._backend.remote_client.manager.specific_prepare()
<commit_msg>Moved OS specific preparation<commit_after># Copyright 2014 Cloudbase Solutions Srl
# All Rights Reserved.
#
#    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.

"""Windows cloudbaseinit recipes."""

import ntpath
import os

import six

from argus import exceptions
from argus.introspection.cloud import windows as introspection
from argus.recipes.cloud import base
from argus import util
from argus.config_generator.windows import cb_init as cbinit_config

LOG = util.get_logger()

# Default values for an instance under booting step.
COUNT = 20
DELAY = 20
_CBINIT_REPO = "https://github.com/openstack/cloudbase-init"


class CloudbaseinitRecipe(base.BaseCloudbaseinitRecipe):
    """Recipe for preparing a Windows instance."""

    def wait_for_boot_completion(self):
        LOG.info("Waiting for first boot completion...")
        self._backend.remote_client.manager.wait_boot_completion()

    def set_mtu(self, interface="ipv4", subinterface_name="Ethernet",
                mtu_value=1400, store_type='active'):
        set_mtu_cmd = ('netsh interface {interface_type} set subinterface '
                       '"{name}" mtu={value} store={type}'
                       .format(interface_type=interface, value=mtu_value,
                               name=subinterface_name, type=store_type))
        LOG.info("Setting the MTU.")
        try:
            self._backend.remote_client.run_command_with_retry(set_mtu_cmd)
        except exceptions.ArgusTimeoutError as exc:
            LOG.debug('Setting MTU failed with %r.' % exc)

    def execution_prologue(self):
        # Prepare Something specific for the OS
        self._backend.remote_client.manager.specific_prepare()

        LOG.info("Retrieve common module for proper script execution.")
        resource_location = "windows/common.psm1"
        self._backend.remote_client.manager.download_resource(
            resource_location=resource_location, location=r'C:\common.psm1')

    def get_installation_script(self):
        """Get instalation script for CloudbaseInit."""
        self._backend.remote_client.manager.get_installation_script()

    def install_cbinit(self):
        """Proceed on checking if cloudbase-init should be installed."""
        try:
            cbdir = introspection.get_cbinit_dir(self._execute)
        except exceptions.ArgusError:
            self._backend.remote_client.manager.install_cbinit()
            self._grab_cbinit_installation_log()
        else:
            # If the directory already exists, we won't be installing Cb-init.
            LOG.info("Cloudbase-init is already installed, skipping installation.")

    def _grab_cbinit_installation_log(self):
        """Obtain the installation logs."""
        LOG.info("Obtaining the installation logs.")
        if not self._conf.argus.output_directory:
            LOG.warning("The output directory wasn't given, "
                        "the log will not be grabbed.")
            return

        content = self._backend.remote_client.read_file("C:\\installation.log")
        log_template = "installation-{}.log".format(
            self._backend.instance_server()['id'])

        path = os.path.join(self._conf.argus.output_directory, log_template)
        with open(path, 'w') as stream:
            stream.write(content)

    def replace_install(self):
        """Replace the cb-init installed files with the downloaded ones.

        For the same file names, there will be a replace. The new ones
        will just be added and the other files will be left there.
        So it's more like an update.
        """
        link = self._conf.argus.patch_install
        if not link:
            return

        LOG.info("Replacing cloudbaseinit's files...")

        LOG.debug("Download and extract installation bundle.")
        if link.startswith("\\\\"):
            cmd = 'copy "{}" "C:\\install.zip"'.format(link)
            self._execute(cmd, command_type=util.CMD)
        else:
            location = r'C:\install.zip'
            self._backend.remote_client.manager.download(
                uri=link, location=location)
        cmds = [
            "Add-Type -A System.IO.Compression.FileSystem",
            "[IO.Compression.ZipFile]::ExtractToDirectory("
            "'C:\\install.zip', 'C:\\install')"
        ]
        cmd = '{}'.format("; ".join(cmds))
        self._execute(cmd, command_type=util.POWERSHELL)

        LOG.debug("Replace old files with the new ones.")
        cbdir = introspection.get_cbinit_dir(self._execute)
        self._execute('xcopy /y /e /q "C:\\install\\Cloudbase-Init"'
                      ' "{}"'.format(cbdir), command_type=util.CMD)

    def replace_code(self):
        """Replace the code of cloudbaseinit."""
        if not self._conf.argus.git_command:
            # Nothing to replace.
            return

        LOG.info("Replacing cloudbaseinit's code...")

        LOG.info("Getting cloudbase-init location...")
        # Get cb-init python location.
        python_dir = introspection.get_python_dir(self._execute)

        # Remove everything from the cloudbaseinit installation.
        LOG.info("Removing recursively cloudbaseinit...")
        cloudbaseinit = ntpath.join(
            python_dir,
            "Lib",
            "site-packages",
            "cloudbaseinit")
        self._execute('rmdir "{}" /S /q'.format(cloudbaseinit),
                      command_type=util.CMD)

        # Clone the repo
        LOG.info("Cloning the cloudbaseinit repo...")
        self._backend.remote_client.manager.git_clone(
            repo_url=_CBINIT_REPO,
            location=r"C:\cloudbaseinit")

        # Run the command provided at cli.
        LOG.info("Applying cli patch...")
        self._execute("cd C:\\cloudbaseinit && {}".format(
            self._conf.argus.git_command), command_type=util.CMD)

        # Replace the code, by moving the code from cloudbaseinit
        # to the installed location.
        LOG.info("Replacing code...")
        self._execute('Copy-Item C:\\cloudbaseinit\\cloudbaseinit '
                      '\'{}\' -Recurse'.format(cloudbaseinit),
                      command_type=util.POWERSHELL)

        # Autoinstall packages from the new requirements.txt
        python = ntpath.join(python_dir, "python.exe")
        command = '"{}" -m pip install -r C:\\cloudbaseinit\\requirements.txt'
        self._execute(command.format(python), command_type=util.CMD)

    def pre_sysprep(self):
        # Patch the installation of cloudbaseinit in order to create
        # a file when the execution ends. We're doing this instead of
        # monitoring the service, because on some OSes, just checking
        # if the service is stopped leads to errors, due to the
        # fact that the service starts later on.
        python_dir = introspection.get_python_dir(self._execute)
        cbinit = ntpath.join(python_dir, 'Lib', 'site-packages',
                             'cloudbaseinit')

        # Get the shell patching script and patch the installation.
        resource_location = "windows/patch_shell.ps1"
        params = r' "{}"'.format(cbinit)
        self._backend.remote_client.manager.execute_powershell_resource_script(
            resource_location=resource_location, parameters=params)

{'fraction_non_alphanumeric': 0.1009711235449225, 'fraction_numerical': 0.002572512701781465, 'mean_word_length': 2.4935969445068524, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 2, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 8, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3842136', 'n_tokens_mistral': 4334, 'n_tokens_neox': 4188, 'n_words': 1129}