Adds Azure guest agent plugin
Adds the Azure guest agent plugin which configures the WindowsAzureGuestAgent, WindowsAzureTelemetryService and RdAgent services. Change-Id: I713c2b17bb083767272cefa2bf517ff1a0cbb992 Co-Authored-By: Stefan Caraiman <scaraiman@cloudbasesolutions.com> Implements: blueprint add-azure-guest-plugin
This commit is contained in:
parent
1581d6f946
commit
f1fb9e4414
@ -160,6 +160,9 @@ class BaseMetadataService(object):
|
||||
def get_server_certs(self):
|
||||
pass
|
||||
|
||||
def get_vm_agent_package_provisioning_data(self):
|
||||
pass
|
||||
|
||||
def get_client_auth_certs(self):
|
||||
pass
|
||||
|
||||
|
@ -181,6 +181,9 @@ class BaseOSUtils(object):
|
||||
"""Enables or disables TRIM delete notifications."""
|
||||
raise NotImplementedError()
|
||||
|
||||
def get_file_version(self, path):
|
||||
raise NotImplementedError()
|
||||
|
||||
def set_path_admin_acls(self, path):
|
||||
raise NotImplementedError()
|
||||
|
||||
|
@ -26,6 +26,7 @@ import pywintypes
|
||||
import six
|
||||
from six.moves import winreg
|
||||
from tzlocal import windows_tz
|
||||
import win32api
|
||||
from win32com import client
|
||||
import win32net
|
||||
import win32netcon
|
||||
@ -1576,3 +1577,26 @@ class WindowsUtils(base.BaseOSUtils):
|
||||
raise exception.CloudbaseInitException(
|
||||
'Failed to take path ownership.\nOutput: %(out)s\nError:'
|
||||
' %(err)s' % {'out': out, 'err': err})
|
||||
|
||||
def check_dotnet_is_installed(self, version):
|
||||
# See: https://msdn.microsoft.com/en-us/library/hh925568(v=vs.110).aspx
|
||||
if str(version) != "4":
|
||||
raise exception.CloudbaseInitException(
|
||||
"Only checking for version 4 is supported at the moment")
|
||||
try:
|
||||
with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, 'SOFTWARE\\'
|
||||
'Microsoft\\NET Framework Setup\\NDP\\'
|
||||
'v%s\\Full' % version) as key:
|
||||
return winreg.QueryValueEx(key, 'Install')[0] != 0
|
||||
except WindowsError as ex:
|
||||
if ex.winerror == 2:
|
||||
return False
|
||||
else:
|
||||
raise
|
||||
|
||||
def get_file_version(self, path):
|
||||
info = win32api.GetFileVersionInfo(path, '\\')
|
||||
ms = info['FileVersionMS']
|
||||
ls = info['FileVersionLS']
|
||||
return (win32api.HIWORD(ms), win32api.LOWORD(ms),
|
||||
win32api.HIWORD(ls), win32api.LOWORD(ls))
|
||||
|
267
cloudbaseinit/plugins/windows/azureguestagent.py
Normal file
267
cloudbaseinit/plugins/windows/azureguestagent.py
Normal file
@ -0,0 +1,267 @@
|
||||
# Copyright (c) 2017 Cloudbase Solutions Srl
|
||||
#
|
||||
# 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.
|
||||
|
||||
import datetime
|
||||
import os
|
||||
import shutil
|
||||
import zipfile
|
||||
|
||||
from oslo_log import log as oslo_logging
|
||||
from six.moves import winreg
|
||||
|
||||
from cloudbaseinit import conf as cloudbaseinit_conf
|
||||
from cloudbaseinit import exception
|
||||
from cloudbaseinit.osutils import factory as osutils_factory
|
||||
from cloudbaseinit.plugins.common import base
|
||||
|
||||
CONF = cloudbaseinit_conf.CONF
|
||||
LOG = oslo_logging.getLogger(__name__)
|
||||
|
||||
SERVICE_NAME_RDAGENT = "RdAgent"
|
||||
SERVICE_NAME_WAGUESTAGENT = "WindowsAzureGuestAgent"
|
||||
SERVICE_NAME_WA_TELEMETRY = "WindowsAzureTelemetryService"
|
||||
RDAGENT_FILENAME = "WaAppAgent.exe"
|
||||
|
||||
GUEST_AGENT_FILENAME = "Microsoft.Azure.Agent.Windows.exe"
|
||||
NANO_VMAGENT_FILENAME = "WaSvc.exe"
|
||||
GUEST_AGENT_EVENTNAME = "Global\AzureAgentStopRequest"
|
||||
|
||||
LOGMAN_TRACE_NOT_RUNNING = 0x80300104
|
||||
LOGMAN_TRACE_NOT_FOUND = 0x80300002
|
||||
|
||||
GUEST_AGENT_ROOT_PATH = "WindowsAzure"
|
||||
PACKAGES_ROOT_PATH = "Packages"
|
||||
GUEST_AGENT_SOURCE_PATH = '$$\\OEM\GuestAgent'
|
||||
|
||||
VM_AGENT_PACKAGE = "VmAgent_Nano.zip"
|
||||
|
||||
|
||||
class AzureGuestAgentPlugin(base.BasePlugin):
|
||||
@staticmethod
|
||||
def _check_delete_service(osutils, service_name):
|
||||
if osutils.check_service_exists(service_name):
|
||||
svc_status = osutils.get_service_status(service_name)
|
||||
if svc_status != osutils.SERVICE_STATUS_STOPPED:
|
||||
osutils.stop_service(service_name, wait=True)
|
||||
osutils.delete_service(service_name)
|
||||
|
||||
@staticmethod
|
||||
def _remove_agent_services(osutils):
|
||||
LOG.info("Stopping and removing any existing Azure guest agent "
|
||||
"services")
|
||||
for service_name in [
|
||||
SERVICE_NAME_RDAGENT, SERVICE_NAME_WAGUESTAGENT,
|
||||
SERVICE_NAME_WA_TELEMETRY]:
|
||||
AzureGuestAgentPlugin._check_delete_service(
|
||||
osutils, service_name)
|
||||
|
||||
@staticmethod
|
||||
def _remove_azure_dirs():
|
||||
for path in [GUEST_AGENT_ROOT_PATH, PACKAGES_ROOT_PATH]:
|
||||
full_path = os.path.join(os.getenv("SystemDrive"), "\\", path)
|
||||
if os.path.exists(full_path):
|
||||
LOG.info("Removing folder: %s", full_path)
|
||||
try:
|
||||
shutil.rmtree(full_path)
|
||||
except Exception as ex:
|
||||
LOG.error("Failed to remove path: %s", full_path)
|
||||
LOG.exception(ex)
|
||||
|
||||
@staticmethod
|
||||
def _set_registry_vm_type(vm_type="IAAS"):
|
||||
with winreg.CreateKey(winreg.HKEY_LOCAL_MACHINE,
|
||||
"SOFTWARE\\Microsoft\\Windows Azure") as key:
|
||||
winreg.SetValueEx(key, "VMType", 0, winreg.REG_SZ, vm_type)
|
||||
|
||||
@staticmethod
|
||||
def _set_registry_ga_params(install_version, install_timestamp):
|
||||
with winreg.CreateKey(winreg.HKEY_LOCAL_MACHINE,
|
||||
"SOFTWARE\\Microsoft\\GuestAgent") as key:
|
||||
|
||||
install_version_str = "%s.%s.%s.%s" % install_version
|
||||
winreg.SetValueEx(
|
||||
key, "Incarnation", 0, winreg.REG_SZ, install_version_str)
|
||||
|
||||
install_timestamp_str = install_timestamp.strftime(
|
||||
'%m/%d/%Y %I:%M:%S %p')
|
||||
winreg.SetValueEx(
|
||||
key, "VmProvisionedAt", 0, winreg.REG_SZ,
|
||||
install_timestamp_str)
|
||||
|
||||
@staticmethod
|
||||
def _configure_vm_agent(osutils, vm_agent_target_path):
|
||||
vm_agent_zip_path = os.path.join(os.getenv("SystemDrive"), '\\',
|
||||
"Windows", "NanoGuestAgent",
|
||||
VM_AGENT_PACKAGE)
|
||||
vm_agent_log_path = os.path.join(os.getenv("SystemDrive"), '\\',
|
||||
GUEST_AGENT_ROOT_PATH, "Logs")
|
||||
if not os.path.exists(vm_agent_log_path):
|
||||
os.makedirs(vm_agent_log_path)
|
||||
with zipfile.ZipFile(vm_agent_zip_path) as zf:
|
||||
zf.extractall(vm_agent_target_path)
|
||||
vm_agent_service_path = os.path.join(
|
||||
vm_agent_target_path, NANO_VMAGENT_FILENAME)
|
||||
vm_agent_service_path = ("{service_path} -name {agent_name} -ownLog "
|
||||
"{log_path}\\W_svc.log -svcLog {log_path}"
|
||||
"\\S_svc.log -event {event_name} -- "
|
||||
"{vm_agent_target_path}\\"
|
||||
"{guest_agent}".format(
|
||||
service_path=vm_agent_service_path,
|
||||
agent_name=SERVICE_NAME_WAGUESTAGENT,
|
||||
log_path=vm_agent_log_path,
|
||||
event_name=GUEST_AGENT_EVENTNAME,
|
||||
vm_agent_target_path=vm_agent_target_path,
|
||||
guest_agent=GUEST_AGENT_FILENAME))
|
||||
|
||||
osutils.create_service(
|
||||
SERVICE_NAME_WAGUESTAGENT, SERVICE_NAME_WAGUESTAGENT,
|
||||
vm_agent_service_path, osutils.SERVICE_START_MODE_MANUAL)
|
||||
|
||||
@staticmethod
|
||||
def _configure_rd_agent(osutils, ga_target_path):
|
||||
rd_agent_service_path = os.path.join(
|
||||
ga_target_path, RDAGENT_FILENAME)
|
||||
# TODO(alexpilotti): Add a retry here as the service could have been
|
||||
# marked for deletion
|
||||
osutils.create_service(
|
||||
SERVICE_NAME_RDAGENT, SERVICE_NAME_RDAGENT,
|
||||
rd_agent_service_path, osutils.SERVICE_START_MODE_MANUAL)
|
||||
|
||||
path = os.path.join(ga_target_path, "TransparentInstaller.dll")
|
||||
ga_version = osutils.get_file_version(path)
|
||||
ga_install_time = datetime.datetime.now()
|
||||
|
||||
AzureGuestAgentPlugin._set_registry_vm_type()
|
||||
AzureGuestAgentPlugin._set_registry_ga_params(
|
||||
ga_version, ga_install_time)
|
||||
|
||||
@staticmethod
|
||||
def _stop_event_trace(osutils, name, ets=False):
|
||||
return AzureGuestAgentPlugin._run_logman(osutils, "stop", name, ets)
|
||||
|
||||
@staticmethod
|
||||
def _delete_event_trace(osutils, name):
|
||||
return AzureGuestAgentPlugin._run_logman(osutils, "delete", name)
|
||||
|
||||
@staticmethod
|
||||
def _run_logman(osutils, action, name, ets=False):
|
||||
args = ["logman.exe"]
|
||||
if ets:
|
||||
args += ["-ets"]
|
||||
args += [action, name]
|
||||
(out, err, ret_val) = osutils.execute_system32_process(args)
|
||||
if ret_val not in [
|
||||
0, LOGMAN_TRACE_NOT_RUNNING, LOGMAN_TRACE_NOT_FOUND]:
|
||||
LOG.error(
|
||||
'logman failed.\nExit code: %(ret_val)s\n'
|
||||
'Output: %(out)s\nError: %(err)s',
|
||||
{'ret_val': hex(ret_val), 'out': out, 'err': err})
|
||||
|
||||
@staticmethod
|
||||
def _stop_ga_event_traces(osutils):
|
||||
LOG.info("Stopping Azure guest agent event traces")
|
||||
AzureGuestAgentPlugin._stop_event_trace(osutils, "GAEvents")
|
||||
AzureGuestAgentPlugin._stop_event_trace(osutils, "RTEvents")
|
||||
AzureGuestAgentPlugin._stop_event_trace(
|
||||
osutils, "WindowsAzure-GuestAgent-Metrics", ets=True)
|
||||
AzureGuestAgentPlugin._stop_event_trace(
|
||||
osutils, "WindowsAzure-GuestAgent-Diagnostic", ets=True)
|
||||
|
||||
@staticmethod
|
||||
def _delete_ga_event_traces(osutils):
|
||||
LOG.info("Deleting Azure guest agent event traces")
|
||||
AzureGuestAgentPlugin._delete_event_trace(osutils, "GAEvents")
|
||||
AzureGuestAgentPlugin._delete_event_trace(osutils, "RTEvents")
|
||||
|
||||
@staticmethod
|
||||
def _get_guest_agent_source_path(osutils):
|
||||
base_paths = osutils.get_logical_drives()
|
||||
for base_path in base_paths:
|
||||
path = os.path.join(base_path, GUEST_AGENT_SOURCE_PATH)
|
||||
if os.path.exists(path):
|
||||
return path
|
||||
raise exception.CloudbaseInitException(
|
||||
"Azure guest agent source folder not found")
|
||||
|
||||
def execute(self, service, shared_data):
|
||||
provisioning_data = service.get_vm_agent_package_provisioning_data()
|
||||
if not provisioning_data:
|
||||
LOG.info("Azure guest agent provisioning data not present")
|
||||
elif not provisioning_data.get("provision"):
|
||||
LOG.info("Skipping Azure guest agent provisioning as by metadata "
|
||||
"request")
|
||||
else:
|
||||
osutils = osutils_factory.get_os_utils()
|
||||
|
||||
self._remove_agent_services(osutils)
|
||||
# TODO(alexpilotti): Check for processes that might still be
|
||||
# running
|
||||
self._remove_azure_dirs()
|
||||
|
||||
if not osutils.is_nano_server():
|
||||
ga_package_name = provisioning_data.get("package_name")
|
||||
if not ga_package_name:
|
||||
raise exception.ItemNotFoundException(
|
||||
"Azure guest agent package_name not found in metadata")
|
||||
LOG.debug("Azure guest agent package name: %s",
|
||||
ga_package_name)
|
||||
|
||||
ga_path = self._get_guest_agent_source_path(osutils)
|
||||
ga_zip_path = os.path.join(ga_path, ga_package_name)
|
||||
if not os.path.exists(ga_zip_path):
|
||||
raise exception.CloudbaseInitException(
|
||||
"Azure guest agent package file not found: %s" %
|
||||
ga_zip_path)
|
||||
|
||||
self._stop_ga_event_traces(osutils)
|
||||
self._delete_ga_event_traces(osutils)
|
||||
|
||||
ga_target_path = os.path.join(
|
||||
os.getenv("SystemDrive"), '\\', GUEST_AGENT_ROOT_PATH,
|
||||
"Packages")
|
||||
|
||||
if os.path.exists(ga_target_path):
|
||||
shutil.rmtree(ga_target_path)
|
||||
os.makedirs(ga_target_path)
|
||||
|
||||
with zipfile.ZipFile(ga_zip_path) as zf:
|
||||
zf.extractall(ga_target_path)
|
||||
|
||||
self._configure_rd_agent(osutils, ga_target_path)
|
||||
|
||||
if not osutils.check_dotnet_is_installed("4"):
|
||||
LOG.warn("The .Net framework 4.5 or greater is required "
|
||||
"by the Azure guest agent")
|
||||
else:
|
||||
osutils.set_service_start_mode(
|
||||
SERVICE_NAME_RDAGENT,
|
||||
osutils.SERVICE_START_MODE_AUTOMATIC)
|
||||
osutils.start_service(SERVICE_NAME_RDAGENT)
|
||||
else:
|
||||
vm_agent_target_path = os.path.join(
|
||||
os.getenv("SystemDrive"), '\\', GUEST_AGENT_ROOT_PATH,
|
||||
"Packages", "GuestAgent")
|
||||
if not os.path.exists(vm_agent_target_path):
|
||||
os.makedirs(vm_agent_target_path)
|
||||
self._configure_vm_agent(osutils, vm_agent_target_path)
|
||||
|
||||
osutils.set_service_start_mode(
|
||||
SERVICE_NAME_WAGUESTAGENT,
|
||||
osutils.SERVICE_START_MODE_AUTOMATIC)
|
||||
osutils.start_service(SERVICE_NAME_WAGUESTAGENT)
|
||||
|
||||
return base.PLUGIN_EXECUTION_DONE, False
|
||||
|
||||
def get_os_requirements(self):
|
||||
return 'win32', (6, 1)
|
@ -52,6 +52,7 @@ class TestWindowsUtils(testutils.CloudbaseInitTestBase):
|
||||
self._pywintypes_mock = mock.MagicMock()
|
||||
self._pywintypes_mock.error = fake.FakeError
|
||||
self._pywintypes_mock.com_error = fake.FakeComError
|
||||
self._win32api_mock = mock.MagicMock()
|
||||
self._win32com_mock = mock.MagicMock()
|
||||
self._win32process_mock = mock.MagicMock()
|
||||
self._win32security_mock = mock.MagicMock()
|
||||
@ -72,7 +73,8 @@ class TestWindowsUtils(testutils.CloudbaseInitTestBase):
|
||||
|
||||
_module_patcher = mock.patch.dict(
|
||||
'sys.modules',
|
||||
{'win32com': self._win32com_mock,
|
||||
{'win32api': self._win32api_mock,
|
||||
'win32com': self._win32com_mock,
|
||||
'win32process': self._win32process_mock,
|
||||
'win32security': self._win32security_mock,
|
||||
'win32net': self._win32net_mock,
|
||||
@ -2552,3 +2554,36 @@ class TestWindowsUtils(testutils.CloudbaseInitTestBase):
|
||||
|
||||
def test_take_path_ownership(self):
|
||||
self._test_take_path_ownership()
|
||||
|
||||
def _test_check_dotnet_is_installed(self, version):
|
||||
if str(version) != "4":
|
||||
self.assertRaises(exception.CloudbaseInitException,
|
||||
self._winutils.check_dotnet_is_installed,
|
||||
version)
|
||||
return
|
||||
res = self._winutils.check_dotnet_is_installed(version)
|
||||
key = self._winreg_mock.OpenKey.return_value.__enter__.return_value
|
||||
self._winreg_mock.OpenKey.assert_called_with(
|
||||
self._winreg_mock.HKEY_LOCAL_MACHINE,
|
||||
'SOFTWARE\\Microsoft\\NET Framework Setup\\NDP\\'
|
||||
'v%s\\Full' % version)
|
||||
self._winreg_mock.QueryValueEx.assert_called_with(
|
||||
key, 'Install')
|
||||
self.assertTrue(res)
|
||||
|
||||
def test_check_dotnet_is_installed_not_v4(self):
|
||||
fake_version = 1
|
||||
self._test_check_dotnet_is_installed(fake_version)
|
||||
|
||||
def test_check_dotnet_is_installed_v4(self):
|
||||
fake_version = 4
|
||||
self._test_check_dotnet_is_installed(fake_version)
|
||||
|
||||
def test_get_file_version(self):
|
||||
mock_path = mock.sentinel.fake_path
|
||||
mock_info = mock.MagicMock()
|
||||
self._win32api_mock.GetFileVersionInfo.return_value = mock_info
|
||||
res = self._winutils.get_file_version(mock_path)
|
||||
self._win32api_mock.GetFileVersionInfo.assert_called_once_with(
|
||||
mock_path, '\\')
|
||||
self.assertIsNotNone(res)
|
||||
|
233
cloudbaseinit/tests/plugins/windows/test_azureguestagent.py
Normal file
233
cloudbaseinit/tests/plugins/windows/test_azureguestagent.py
Normal file
@ -0,0 +1,233 @@
|
||||
# Copyright (c) 2017 Cloudbase Solutions Srl
|
||||
#
|
||||
# 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.
|
||||
|
||||
import datetime
|
||||
import importlib
|
||||
import os
|
||||
import unittest
|
||||
|
||||
try:
|
||||
import unittest.mock as mock
|
||||
except ImportError:
|
||||
import mock
|
||||
|
||||
from cloudbaseinit import conf as cloudbaseinit_conf
|
||||
from cloudbaseinit import exception
|
||||
from cloudbaseinit.plugins.common import base
|
||||
from cloudbaseinit.tests import testutils
|
||||
|
||||
CONF = cloudbaseinit_conf.CONF
|
||||
MODPATH = "cloudbaseinit.plugins.windows.azureguestagent"
|
||||
|
||||
|
||||
class AzureGuestAgentPluginTest(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.mock_wmi = mock.MagicMock()
|
||||
self._moves_mock = mock.MagicMock()
|
||||
patcher = mock.patch.dict(
|
||||
"sys.modules",
|
||||
{
|
||||
"wmi": self.mock_wmi,
|
||||
"six.moves": self._moves_mock
|
||||
}
|
||||
)
|
||||
patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
self._winreg_mock = self._moves_mock.winreg
|
||||
self._azureguestagent = importlib.import_module(MODPATH)
|
||||
self._azureagentplugin = self._azureguestagent.AzureGuestAgentPlugin()
|
||||
self.snatcher = testutils.LogSnatcher(MODPATH)
|
||||
|
||||
def test_check_delete_service(self):
|
||||
mock_osutils = mock.Mock()
|
||||
mock_service_name = mock.sentinel.name
|
||||
self._azureagentplugin._check_delete_service(mock_osutils,
|
||||
mock_service_name)
|
||||
mock_osutils.check_service_exists.assert_called_once_with(
|
||||
mock_service_name)
|
||||
mock_osutils.get_service_status.assert_called_once_with(
|
||||
mock_service_name)
|
||||
mock_osutils.stop_service.assert_called_once_with(mock_service_name,
|
||||
wait=True)
|
||||
mock_osutils.delete_service.assert_called_once_with(mock_service_name)
|
||||
|
||||
@mock.patch(MODPATH + ".AzureGuestAgentPlugin._check_delete_service")
|
||||
def test_remove_agent_services(self, mock_check_delete_service):
|
||||
mock_osutils = mock.Mock()
|
||||
expected_logs = ["Stopping and removing any existing Azure guest "
|
||||
"agent services"]
|
||||
with self.snatcher:
|
||||
self._azureagentplugin._remove_agent_services(mock_osutils)
|
||||
self.assertEqual(self.snatcher.output, expected_logs)
|
||||
self.assertEqual(mock_check_delete_service.call_count, 3)
|
||||
|
||||
@mock.patch("shutil.rmtree")
|
||||
@mock.patch("os.path.exists")
|
||||
@mock.patch("os.getenv")
|
||||
def test_remove_azure_dirs(self, mock_os_getenv,
|
||||
mock_exists, mock_rmtree):
|
||||
mock_rmtree.side_effect = (None, Exception)
|
||||
mock_exists.return_value = True
|
||||
with self.snatcher:
|
||||
self._azureagentplugin._remove_azure_dirs()
|
||||
mock_os_getenv.assert_called_with("SystemDrive")
|
||||
self.assertEqual(mock_os_getenv.call_count, 2)
|
||||
self.assertEqual(mock_exists.call_count, 2)
|
||||
self.assertEqual(mock_rmtree.call_count, 2)
|
||||
|
||||
def test_set_registry_vm_type(self):
|
||||
vm_type = mock.sentinel.vm
|
||||
key_name = "SOFTWARE\\Microsoft\\Windows Azure"
|
||||
|
||||
self._azureagentplugin._set_registry_vm_type(vm_type)
|
||||
key = self._winreg_mock.CreateKey.return_value.__enter__.return_value
|
||||
self._winreg_mock.CreateKey.assert_called_with(
|
||||
self._winreg_mock.HKEY_LOCAL_MACHINE, key_name)
|
||||
self._winreg_mock.SetValueEx.assert_called_once_with(
|
||||
key, "VMType", 0, self._winreg_mock.REG_SZ, vm_type)
|
||||
|
||||
def test_set_registry_ga_params(self):
|
||||
fake_version = (1, 2, 3, 4)
|
||||
fake_install_timestamp = datetime.datetime.now()
|
||||
key_name = "SOFTWARE\\Microsoft\\GuestAgent"
|
||||
|
||||
self._azureagentplugin._set_registry_ga_params(fake_version,
|
||||
fake_install_timestamp)
|
||||
|
||||
self._winreg_mock.CreateKey.assert_called_with(
|
||||
self._winreg_mock.HKEY_LOCAL_MACHINE, key_name)
|
||||
self.assertEqual(self._winreg_mock.SetValueEx.call_count, 2)
|
||||
|
||||
@mock.patch(MODPATH + ".AzureGuestAgentPlugin._set_registry_ga_params")
|
||||
@mock.patch(MODPATH + ".AzureGuestAgentPlugin._set_registry_vm_type")
|
||||
def test_configure_rd_agent(self, mock_set_registry_vm_type,
|
||||
mock_set_registry_ga_params):
|
||||
mock_osutils = mock.Mock()
|
||||
fake_ga_path = "C:\\"
|
||||
expected_rd_path = os.path.join(fake_ga_path,
|
||||
self._azureguestagent.RDAGENT_FILENAME)
|
||||
expected_path = os.path.join(fake_ga_path, "TransparentInstaller.dll")
|
||||
self._azureagentplugin._configure_rd_agent(mock_osutils, fake_ga_path)
|
||||
mock_osutils.create_service.assert_called_once_with(
|
||||
self._azureguestagent.SERVICE_NAME_RDAGENT,
|
||||
self._azureguestagent.SERVICE_NAME_RDAGENT,
|
||||
expected_rd_path,
|
||||
mock_osutils.SERVICE_START_MODE_MANUAL)
|
||||
mock_osutils.get_file_version.assert_called_once_with(expected_path)
|
||||
mock_set_registry_vm_type.assert_called_once_with()
|
||||
|
||||
@mock.patch(MODPATH + ".AzureGuestAgentPlugin._run_logman")
|
||||
def test_stop_event_trace(self, mock_run_logman):
|
||||
mock_osutils = mock.Mock()
|
||||
fake_name = mock.sentinel.event_name
|
||||
res = self._azureagentplugin._stop_event_trace(mock_osutils, fake_name)
|
||||
mock_run_logman.assert_called_once_with(mock_osutils, "stop",
|
||||
fake_name, False)
|
||||
self.assertIsNotNone(res)
|
||||
|
||||
@mock.patch(MODPATH + ".AzureGuestAgentPlugin._run_logman")
|
||||
def test_delete_event_trace(self, mock_run_logman):
|
||||
mock_osutils = mock.Mock()
|
||||
fake_name = mock.sentinel.event_name
|
||||
res = self._azureagentplugin._delete_event_trace(mock_osutils,
|
||||
fake_name)
|
||||
mock_run_logman.assert_called_once_with(mock_osutils, "delete",
|
||||
fake_name)
|
||||
self.assertIsNotNone(res)
|
||||
|
||||
def test_run_logman(self):
|
||||
mock_osutils = mock.Mock()
|
||||
fake_action = mock.sentinel.action
|
||||
fake_name = mock.sentinel.cmd_name
|
||||
expected_args = ["logman.exe", "-ets", fake_action, fake_name]
|
||||
mock_osutils.execute_system32_process.return_value = (0, 0, -1)
|
||||
self._azureagentplugin._run_logman(mock_osutils, fake_action,
|
||||
fake_name, True)
|
||||
mock_osutils.execute_system32_process.assert_called_once_with(
|
||||
expected_args)
|
||||
|
||||
@mock.patch(MODPATH + ".AzureGuestAgentPlugin._stop_event_trace")
|
||||
def test_stop_ga_event_traces(self, mock_stop_event_trace):
|
||||
mock_osutils = mock.Mock()
|
||||
expected_logs = ["Stopping Azure guest agent event traces"]
|
||||
with self.snatcher:
|
||||
self._azureagentplugin._stop_ga_event_traces(mock_osutils)
|
||||
self.assertEqual(mock_stop_event_trace.call_count, 4)
|
||||
self.assertEqual(self.snatcher.output, expected_logs)
|
||||
|
||||
@mock.patch(MODPATH + ".AzureGuestAgentPlugin._delete_event_trace")
|
||||
def test_delete_ga_event_traces(self, mock_delete_event_trace):
|
||||
mock_osutils = mock.Mock()
|
||||
expected_logs = ["Deleting Azure guest agent event traces"]
|
||||
with self.snatcher:
|
||||
self._azureagentplugin._delete_ga_event_traces(mock_osutils)
|
||||
self.assertEqual(mock_delete_event_trace.call_count, 2)
|
||||
self.assertEqual(self.snatcher.output, expected_logs)
|
||||
|
||||
@mock.patch("os.path.exists")
|
||||
def _test_get_guest_agent_source_path(self, mock_exists,
|
||||
drives=None, exists=False):
|
||||
mock_osutils = mock.Mock()
|
||||
mock_exists.return_value = exists
|
||||
mock_osutils.get_logical_drives.return_value = drives
|
||||
if not exists:
|
||||
self.assertRaises(
|
||||
exception.CloudbaseInitException,
|
||||
self._azureagentplugin._get_guest_agent_source_path,
|
||||
mock_osutils)
|
||||
return
|
||||
res = self._azureagentplugin._get_guest_agent_source_path(mock_osutils)
|
||||
self.assertIsNotNone(res)
|
||||
|
||||
def test_get_guest_agent_source_path_no_agent(self):
|
||||
self._test_get_guest_agent_source_path(drives=[])
|
||||
|
||||
def test_get_guest_agent_source_path(self):
|
||||
mock_drive = "C:"
|
||||
self._test_get_guest_agent_source_path(drives=[mock_drive],
|
||||
exists=True)
|
||||
|
||||
def _test_execute(self,
|
||||
provisioning_data=None, expected_logs=None):
|
||||
mock_service = mock.Mock()
|
||||
mock_sharedata = mock.Mock()
|
||||
expected_res = (base.PLUGIN_EXECUTION_DONE, False)
|
||||
(mock_service.get_vm_agent_package_provisioning_data.
|
||||
return_value) = provisioning_data
|
||||
if not provisioning_data or not provisioning_data.get("provision"):
|
||||
with self.snatcher:
|
||||
res = self._azureagentplugin.execute(mock_service,
|
||||
mock_sharedata)
|
||||
(mock_service.get_vm_agent_package_provisioning_data.
|
||||
assert_called_once_with())
|
||||
self.assertEqual(res, expected_res)
|
||||
self.assertEqual(self.snatcher.output, expected_logs)
|
||||
return
|
||||
|
||||
def test_execute_no_data(self):
|
||||
expected_logs = ["Azure guest agent provisioning data not present"]
|
||||
self._test_execute(expected_logs=expected_logs)
|
||||
|
||||
def test_execute_no_provision(self):
|
||||
mock_data = {"provision": None}
|
||||
expected_logs = ["Skipping Azure guest agent provisioning "
|
||||
"as by metadata request"]
|
||||
self._test_execute(provisioning_data=mock_data,
|
||||
expected_logs=expected_logs)
|
||||
|
||||
def test_get_os_requirements(self):
|
||||
expected_res = ('win32', (6, 1))
|
||||
res = self._azureagentplugin.get_os_requirements()
|
||||
self.assertEqual(res, expected_res)
|
Loading…
x
Reference in New Issue
Block a user