diff --git a/cloudbaseinit/plugins/windows/userdata-plugins/__init__.py b/cloudbaseinit/plugins/windows/userdata-plugins/__init__.py
new file mode 100644
index 00000000..7227b295
--- /dev/null
+++ b/cloudbaseinit/plugins/windows/userdata-plugins/__init__.py
@@ -0,0 +1,15 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 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.
diff --git a/cloudbaseinit/tests/__init__.py b/cloudbaseinit/tests/__init__.py
new file mode 100644
index 00000000..7227b295
--- /dev/null
+++ b/cloudbaseinit/tests/__init__.py
@@ -0,0 +1,15 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 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.
diff --git a/cloudbaseinit/tests/metadata/__init__.py b/cloudbaseinit/tests/metadata/__init__.py
new file mode 100644
index 00000000..7227b295
--- /dev/null
+++ b/cloudbaseinit/tests/metadata/__init__.py
@@ -0,0 +1,15 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 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.
diff --git a/cloudbaseinit/tests/metadata/fake_json_response.py b/cloudbaseinit/tests/metadata/fake_json_response.py
new file mode 100644
index 00000000..5b51feb6
--- /dev/null
+++ b/cloudbaseinit/tests/metadata/fake_json_response.py
@@ -0,0 +1,57 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 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.
+
+
+def get_fake_metadata_json(version):
+ if version == '2013-04-04':
+ return {"random_seed":
+ "Wn51FGjZa3vlZtTxJuPr96oCf+X8jqbA9U2XR5wNdnApy1fz"
+ "/2NNssUwPoNzG6etw9RBn+XiZ0zKWnFzMsTopaN7WwYjWTnIsVw3cpIk"
+ "Td579wQgoEr1ANqhfO3qTvkOVNMhzTAw1ps+wqRmkLxH+1qYJnX06Gcd"
+ "KRRGkWTaOSlTkieA0LO2oTGFlbFDWcOW2vT5BvSBmqP7vNLzbLDMTc7M"
+ "IWRBzwmtcVPC17QL6EhZJTUcZ0mTz7l0R0DocLmFwHEXFEEr+q4WaJjt"
+ "1ejOOxVM3tiT7D8YpRZnnGNPfvEhq1yVMUoi8yv9pFmMmXicNBhm6zDK"
+ "VjcWk0gfbvaQcMnnOLrrE1VxAAzyNyPIXBI/H7AAHz2ECz7dgd2/4ocv"
+ "3bmTRY3hhcUKtNuat2IOvSGgMBUGdWnLorQGFz8t0/bcYhE0Dve35U6H"
+ "mtj78ydV/wmQWG0iq49NX6hk+VUmZtSZztlkbsaa7ajNjZ+Md9oZtlhX"
+ "Z5vJuhRXnHiCm7dRNO8Xo6HffEBH5A4smQ1T2Kda+1c18DZrY7+iQJRi"
+ "fa6witPCw0tXkQ6nlCLqL2weJD1XMiTZLSM/XsZFGGSkKCKvKLEqQrI/"
+ "XFUq/TA6B4aLGFlmmhOO/vMJcht06O8qVU/xtd5Mv/MRFzYaSG568Z/m"
+ "hk4vYLYdQYAA+pXRW9A=",
+ "uuid": "4b32ddf7-7941-4c36-a854-a1f5ac45b318",
+ "availability_zone": "nova",
+ "hostname": "windows.novalocal",
+ "launch_index": 0,
+ "public_keys": {"key": "ssh-rsa "
+ "AAAAB3NzaC1yc2EAAAADAQABAAABA"
+ "QDf7kQHq7zvBod3yIZs0tB/AOOZz5pab7qt/h"
+ "78VF7yi6qTsFdUnQxRue43R/75wa9EEyokgYR"
+ "LKIN+Jq2A5tXNMcK+rNOCzLJFtioAwEl+S6VL"
+ "G9jfkbUv++7zoSMOsanNmEDvG0B79MpyECFCl"
+ "th2DsdE4MQypify35U5ri5Qi7E6PEYAsU65LF"
+ "MG2boeCIB29BEooE6AgPr2DuJeJ+2uw+YScF9"
+ "FV3og4Wyz5zipPVh8YpVev6dlg0tRWUrCtZF9"
+ "IODpCTrT3vsPRG3xz7CppR+vGi/1gLXHtJCRj"
+ "frHwkY6cXyhypNmkU99K/wMqSv30vsDwdnsQ1"
+ "q3YhLarMHB Generated by Nova\n",
+ "name": "windows"},
+ "network_config": {"content_path": "network",
+ 'debian_config': 'iface eth0 inet static'
+ 'address 10.11.12.13'
+ 'broadcast 0.0.0.0'
+ 'netmask 255.255.255.255'
+ 'gateway 1.2.3.4'
+ 'dns-nameserver 8.8.8.8'}}
diff --git a/cloudbaseinit/tests/metadata/services/__init__.py b/cloudbaseinit/tests/metadata/services/__init__.py
new file mode 100644
index 00000000..7227b295
--- /dev/null
+++ b/cloudbaseinit/tests/metadata/services/__init__.py
@@ -0,0 +1,15 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 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.
diff --git a/cloudbaseinit/tests/metadata/services/configdrive/__init__.py b/cloudbaseinit/tests/metadata/services/configdrive/__init__.py
new file mode 100644
index 00000000..7227b295
--- /dev/null
+++ b/cloudbaseinit/tests/metadata/services/configdrive/__init__.py
@@ -0,0 +1,15 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 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.
diff --git a/cloudbaseinit/tests/metadata/services/configdrive/test_configdrive.py b/cloudbaseinit/tests/metadata/services/configdrive/test_configdrive.py
new file mode 100644
index 00000000..dd28414b
--- /dev/null
+++ b/cloudbaseinit/tests/metadata/services/configdrive/test_configdrive.py
@@ -0,0 +1,84 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 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 importlib
+import mock
+import os
+import sys
+import unittest
+import uuid
+
+from cloudbaseinit.openstack.common import cfg
+
+CONF = cfg.CONF
+_win32com_mock = mock.MagicMock()
+_ctypes_mock = mock.MagicMock()
+_ctypes_util_mock = mock.MagicMock()
+_win32com_client_mock = mock.MagicMock()
+_pywintypes_mock = mock.MagicMock()
+_mock_dict = {'win32com': _win32com_mock,
+ 'ctypes': _ctypes_mock,
+ 'ctypes.util': _ctypes_util_mock,
+ 'win32com.client': _win32com_client_mock,
+ 'pywintypes': _pywintypes_mock}
+
+
+class ConfigDriveServiceTest(unittest.TestCase):
+ @mock.patch.dict(sys.modules, _mock_dict)
+ def setUp(self):
+ configdrive = importlib.import_module('cloudbaseinit.metadata.services'
+ '.configdrive.configdrive')
+ self._config_drive = configdrive.ConfigDriveService()
+
+ def tearDown(self):
+ reload(sys)
+
+ @mock.patch('cloudbaseinit.metadata.services.configdrive.manager.'
+ 'ConfigDriveManager.get_config_drive_files')
+ @mock.patch('tempfile.gettempdir')
+ @mock.patch('os.path.join')
+ def test_load(self, mock_join, mock_gettempdir,
+ mock_get_config_drive_files):
+ uuid.uuid4 = mock.MagicMock()
+ fake_path = os.path.join('fake', 'path')
+ fake_path_found = os.path.join(fake_path, 'found')
+ uuid.uuid4.return_value = 'random'
+ mock_get_config_drive_files.return_value = fake_path_found
+ mock_join.return_value = fake_path
+ response = self._config_drive.load()
+ mock_join.assert_called_with(mock_gettempdir(), 'random')
+ mock_get_config_drive_files.assert_called_once_with(
+ fake_path, CONF.config_drive_raw_hhd, CONF.config_drive_cdrom)
+ self.assertEqual(self._config_drive._metadata_path, fake_path)
+ self.assertEqual(response, fake_path_found)
+
+ @mock.patch('os.path.normpath')
+ @mock.patch('os.path.join')
+ def test_get_data(self, mock_join, mock_normpath):
+ fake_path = os.path.join('fake', 'path')
+ with mock.patch('__builtin__.open',
+ mock.mock_open(read_data='fake data'), create=True):
+ response = self._config_drive._get_data(fake_path)
+ self.assertEqual(response, 'fake data')
+ mock_join.assert_called_with(
+ self._config_drive._metadata_path, fake_path)
+
+ @mock.patch('shutil.rmtree')
+ def test_cleanup(self, mock_rmtree):
+ fake_path = os.path.join('fake', 'path')
+ self._config_drive._metadata_path = fake_path
+ self._config_drive.cleanup()
+ self.assertEqual(self._config_drive._metadata_path, None)
diff --git a/cloudbaseinit/tests/metadata/services/test_ec2service.py b/cloudbaseinit/tests/metadata/services/test_ec2service.py
new file mode 100644
index 00000000..2fa4f004
--- /dev/null
+++ b/cloudbaseinit/tests/metadata/services/test_ec2service.py
@@ -0,0 +1,144 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 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 mock
+import os
+import unittest
+
+from cloudbaseinit.metadata.services import ec2service
+from cloudbaseinit.openstack.common import cfg
+
+CONF = cfg.CONF
+
+
+class Ec2ServiceTest(unittest.TestCase):
+
+ def setUp(self):
+ self._ec2service = ec2service.EC2Service()
+
+ @mock.patch('cloudbaseinit.metadata.services.ec2service.EC2Service'
+ '.get_meta_data')
+ def _test_load(self, mock_get_meta_data, side_effect):
+ mock_get_meta_data.side_effect = [side_effect]
+ response = self._ec2service.load()
+ mock_get_meta_data.assert_called_once_with('openstack')
+ if side_effect is Exception:
+ self.assertFalse(response)
+ else:
+ self.assertTrue(response)
+
+ def test_load_exception(self):
+ self._test_load(side_effect=Exception)
+
+ def test_load(self):
+ self._test_load(side_effect='fake data')
+
+ @mock.patch('posixpath.join')
+ @mock.patch('urllib2.Request')
+ @mock.patch('urllib2.urlopen')
+ @mock.patch('cloudbaseinit.metadata.services.ec2service.EC2Service'
+ '._load_public_keys')
+ @mock.patch('cloudbaseinit.metadata.services.ec2service.EC2Service'
+ '._check_EC2')
+ @mock.patch('cloudbaseinit.metadata.services.ec2service.EC2Service'
+ '._get_EC2_value')
+ def _test_get_data(self, mock_get_EC2_value, mock_check_EC2,
+ mock_load_public_keys, mock_urlopen,
+ mock_Request, mock_join, check_ec2, data_type):
+ mock_path = mock.MagicMock()
+ mock_req = mock.MagicMock()
+ mock_response = mock.MagicMock()
+ fake_path = os.path.join('fake', 'path')
+ mock_join.return_value = fake_path
+ mock_check_EC2.return_value = check_ec2
+ mock_Request.return_value = mock_req
+ mock_urlopen.return_value = mock_response
+ mock_response.read.return_value = 'fake data'
+ mock_path.endswith.return_value = data_type
+
+ if check_ec2 is None:
+ self.assertRaises(Exception, self._ec2service._get_data,
+ mock_path)
+
+ elif data_type is 'meta_data.json':
+ response = self._ec2service._get_data(mock_path)
+ print response
+ for key in ec2service.ec2nodes:
+ mock_get_EC2_value.assert_called_with(key)
+ mock_load_public_keys.assert_called_with()
+
+ elif data_type is 'user_data':
+ response = self._ec2service._get_data(mock_path)
+ mock_join.assert_called_with(CONF.ec2_metadata_base_url,
+ 'user-data')
+ mock_Request.assert_called_once_with(fake_path)
+ mock_urlopen.assert_called_once_with(mock_req)
+ mock_response.read.assert_called_once_with()
+ self.assertEqual(response, 'fake data')
+
+ def test_get_data_metadata_json(self):
+ self._test_get_data(check_ec2=True, data_type='meta_data.json')
+
+ def test_get_data_user_data(self):
+ self._test_get_data(check_ec2=True, data_type='user_data')
+
+ def test_get_data_no_EC2(self):
+ self._test_get_data(check_ec2=None, data_type=None)
+
+ @mock.patch('cloudbaseinit.metadata.services.ec2service.EC2Service'
+ '._get_EC2_value')
+ def _test_check_EC2(self, mock_get_EC2_value, side_effect):
+ mock_get_EC2_value.side_effect = [side_effect]
+ response = self._ec2service._check_EC2()
+ if side_effect is Exception:
+ self.assertFalse(response)
+ else:
+ self.assertTrue(response)
+
+ def test_check_EC2_Exception(self):
+ self._test_check_EC2(side_effect=Exception)
+
+ def test_check_EC2(self):
+ self._test_check_EC2(side_effect='fake value')
+
+ @mock.patch('posixpath.join')
+ @mock.patch('urllib2.Request')
+ @mock.patch('urllib2.urlopen')
+ def test_get_EC2_value(self, mock_urlopen, mock_Request, mock_join):
+ mock_key = mock.MagicMock()
+ mock_response = mock.MagicMock()
+ fake_path = os.path.join('fake', 'path')
+ mock_join.return_value = fake_path
+ mock_Request.return_value = 'fake req'
+ mock_urlopen.return_value = mock_response
+ mock_response.read.return_value = 'fake data'
+ response = self._ec2service._get_EC2_value(mock_key)
+ mock_join.assert_called_with(CONF.ec2_metadata_base_url,
+ 'meta-data', mock_key)
+ mock_Request.assert_called_once_with(fake_path)
+ mock_urlopen.assert_called_once_with('fake req')
+ mock_response.read.assert_called_once_with()
+ self.assertEqual(response, 'fake data')
+
+ @mock.patch('cloudbaseinit.metadata.services.ec2service.EC2Service'
+ '._get_EC2_value')
+ def test_load_public_keys(self, mock_get_EC2_value):
+ data = {}
+ key_list = mock.MagicMock()
+ mock_get_EC2_value.return_value = key_list
+ self._ec2service._load_public_keys(data)
+ mock_get_EC2_value.assert_called_with('public-keys/')
+ self.assertEqual(data['public_keys'], {})
diff --git a/cloudbaseinit/tests/metadata/services/test_httpservice.py b/cloudbaseinit/tests/metadata/services/test_httpservice.py
new file mode 100644
index 00000000..97899836
--- /dev/null
+++ b/cloudbaseinit/tests/metadata/services/test_httpservice.py
@@ -0,0 +1,156 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 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 mock
+import os
+import unittest
+import urllib2
+
+from cloudbaseinit.metadata.services import base
+from cloudbaseinit.metadata.services import httpservice
+from cloudbaseinit.openstack.common import cfg
+
+CONF = cfg.CONF
+
+
+class HttpServiceTest(unittest.TestCase):
+ def setUp(self):
+ CONF.set_override('retry_count_interval', 0)
+ self._httpservice = httpservice.HttpService()
+
+ @mock.patch('cloudbaseinit.osutils.factory.OSUtilsFactory.get_os_utils')
+ @mock.patch('urlparse.urlparse')
+ def _test_check_metadata_ip_route(self, mock_urlparse, mock_get_os_utils,
+ side_effect):
+ mock_utils = mock.MagicMock()
+ mock_split = mock.MagicMock()
+ mock_get_os_utils.return_value = mock_utils
+ mock_utils.check_os_version.return_value = True
+ mock_urlparse().netloc.split.return_value = mock_split
+ mock_split[0].startswith.return_value = True
+ mock_utils.check_static_route_exists.return_value = False
+ mock_utils.get_default_gateway.return_value = (1, '0.0.0.0')
+ mock_utils.add_static_route.side_effect = [side_effect]
+ self._httpservice._check_metadata_ip_route()
+ mock_utils.check_os_version.assert_called_once_with(6, 0)
+ mock_urlparse.assert_called_with(CONF.metadata_base_url)
+ mock_split[0].startswith.assert_called_once_with("169.254.")
+ mock_utils.check_static_route_exists.assert_called_once_with(
+ mock_split[0])
+ mock_utils.get_default_gateway.assert_called_once_with()
+ mock_utils.add_static_route.assert_called_once_with(
+ mock_split[0], "255.255.255.255", '0.0.0.0', 1, 10)
+
+ def test_test_check_metadata_ip_route(self):
+ self._test_check_metadata_ip_route(side_effect=None)
+
+ def test_test_check_metadata_ip_route_fail(self):
+ self._test_check_metadata_ip_route(side_effect=Exception)
+
+ @mock.patch('cloudbaseinit.metadata.services.httpservice.HttpService'
+ '._check_metadata_ip_route')
+ @mock.patch('cloudbaseinit.metadata.services.httpservice.HttpService'
+ '.get_meta_data')
+ def _test_load(self, mock_get_meta_data, mock_check_metadata_ip_route,
+ side_effect):
+ mock_get_meta_data.side_effect = [side_effect]
+ response = self._httpservice.load()
+ mock_check_metadata_ip_route.assert_called_once_with()
+ mock_get_meta_data.assert_called_once_with('openstack')
+ if side_effect:
+ self.assertEqual(response, False)
+ else:
+ self.assertEqual(response, True)
+
+ def test_load(self):
+ self._test_load(side_effect=None)
+
+ def test_load_exception(self):
+ self._test_load(side_effect=Exception)
+
+ @mock.patch('urllib2.urlopen')
+ def _test_get_response(self, mock_urlopen, side_effect):
+ mock_req = mock.MagicMock
+ if side_effect and side_effect.code is 404:
+ mock_urlopen.side_effect = [side_effect]
+ self.assertRaises(base.NotExistingMetadataException,
+ self._httpservice._get_response,
+ mock_req)
+ elif side_effect and side_effect.code:
+ mock_urlopen.side_effect = [side_effect]
+ self.assertRaises(Exception, self._httpservice._get_response,
+ mock_req)
+ else:
+ mock_urlopen.return_value = 'fake url'
+ response = self._httpservice._get_response(mock_req)
+ self.assertEqual(response, 'fake url')
+
+ def test_get_response_fail_HTTPError(self):
+ error = urllib2.HTTPError("http://169.254.169.254/", 404,
+ 'test error 404', {}, None)
+ self._test_get_response(side_effect=error)
+
+ def test_get_response_fail_other_exception(self):
+ error = urllib2.HTTPError("http://169.254.169.254/", 409,
+ 'test error 409', {}, None)
+ self._test_get_response(side_effect=error)
+
+ def test_get_response(self):
+ self._test_get_response(side_effect=None)
+
+ @mock.patch('cloudbaseinit.metadata.services.httpservice.HttpService'
+ '._get_response')
+ @mock.patch('posixpath.join')
+ @mock.patch('urllib2.Request')
+ def test_get_data(self, mock_Request, mock_posix_join,
+ mock_get_response):
+ fake_path = os.path.join('fake', 'path')
+ mock_data = mock.MagicMock()
+ mock_norm_path = mock.MagicMock()
+ mock_req = mock.MagicMock()
+ mock_get_response.return_value = mock_data
+ mock_posix_join.return_value = mock_norm_path
+ mock_Request.return_value = mock_req
+
+ response = self._httpservice._get_data(fake_path)
+
+ mock_posix_join.assert_called_with(CONF.metadata_base_url, fake_path)
+ mock_Request.assert_called_once_with(mock_norm_path)
+ mock_get_response.assert_called_once_with(mock_req)
+ self.assertEqual(response, mock_data.read())
+
+ @mock.patch('cloudbaseinit.metadata.services.httpservice.HttpService'
+ '._get_response')
+ @mock.patch('posixpath.join')
+ @mock.patch('urllib2.Request')
+ def test_post_data(self, mock_Request, mock_posix_join,
+ mock_get_response):
+ fake_path = os.path.join('fake', 'path')
+ fake_data = 'fake data'
+ mock_data = mock.MagicMock()
+ mock_norm_path = mock.MagicMock()
+ mock_req = mock.MagicMock()
+ mock_get_response.return_value = mock_data
+ mock_posix_join.return_value = mock_norm_path
+ mock_Request.return_value = mock_req
+
+ response = self._httpservice._post_data(fake_path, fake_data)
+
+ mock_posix_join.assert_called_with(CONF.metadata_base_url,
+ fake_path)
+ mock_Request.assert_called_once_with(mock_norm_path, data=fake_data)
+ mock_get_response.assert_called_once_with(mock_req)
+ self.assertEqual(response, True)
diff --git a/cloudbaseinit/tests/metadata/test_factory.py b/cloudbaseinit/tests/metadata/test_factory.py
new file mode 100644
index 00000000..ba1de9a1
--- /dev/null
+++ b/cloudbaseinit/tests/metadata/test_factory.py
@@ -0,0 +1,41 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 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 mock
+import unittest
+
+from cloudbaseinit.metadata import factory
+
+
+class MetadataServiceFactoryTests(unittest.TestCase):
+ def setUp(self):
+ self._factory = factory.MetadataServiceFactory()
+
+ @mock.patch('cloudbaseinit.utils.classloader.ClassLoader.load_class')
+ def _test_get_metadata_service(self, mock_load_class, ret_value):
+ mock_load_class.side_effect = ret_value
+ if ret_value is Exception:
+ self.assertRaises(Exception, self._factory.get_metadata_service)
+ else:
+ response = self._factory.get_metadata_service()
+ self.assertEqual(response, mock_load_class()())
+
+ def test_get_metadata_service(self):
+ m = mock.MagicMock()
+ self._test_get_metadata_service(ret_value=m)
+
+ def test_get_metadata_service_exception(self):
+ self._test_get_metadata_service(ret_value=Exception)
diff --git a/cloudbaseinit/tests/osutils/__init__.py b/cloudbaseinit/tests/osutils/__init__.py
new file mode 100644
index 00000000..7227b295
--- /dev/null
+++ b/cloudbaseinit/tests/osutils/__init__.py
@@ -0,0 +1,15 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 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.
diff --git a/cloudbaseinit/tests/osutils/test_factory.py b/cloudbaseinit/tests/osutils/test_factory.py
new file mode 100644
index 00000000..8b2b90c1
--- /dev/null
+++ b/cloudbaseinit/tests/osutils/test_factory.py
@@ -0,0 +1,43 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 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 mock
+import os
+import unittest
+
+from cloudbaseinit.osutils import factory
+
+
+class OSUtilsFactory(unittest.TestCase):
+ def setUp(self):
+ self._factory = factory.OSUtilsFactory()
+
+ @mock.patch('cloudbaseinit.utils.classloader.ClassLoader.load_class')
+ def _test_get_os_utils(self, mock_load_class, fake_name):
+ os.name = fake_name
+ self._factory.get_os_utils()
+ if fake_name == 'nt':
+ mock_load_class.assert_called_with(
+ 'cloudbaseinit.osutils.windows.WindowsUtils')
+ elif fake_name == 'posix':
+ mock_load_class.assert_called_with(
+ 'cloudbaseinit.osutils.posix.PosixUtils')
+
+ def test_get_os_utils_windows(self):
+ self._test_get_os_utils(fake_name='nt')
+
+ def test_get_os_utils_posix(self):
+ self._test_get_os_utils(fake_name='posix')
diff --git a/cloudbaseinit/tests/osutils/test_windows.py b/cloudbaseinit/tests/osutils/test_windows.py
new file mode 100644
index 00000000..675e1987
--- /dev/null
+++ b/cloudbaseinit/tests/osutils/test_windows.py
@@ -0,0 +1,975 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 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 ctypes
+import mock
+import time
+import sys
+import unittest
+
+if sys.platform == 'win32':
+ import _winreg
+ import win32process
+ import win32security
+ import wmi
+
+ from ctypes import windll
+ from ctypes import wintypes
+ from cloudbaseinit.osutils import windows as windows_utils
+from cloudbaseinit.openstack.common import cfg
+
+CONF = cfg.CONF
+
+
+@unittest.skipUnless(sys.platform == "win32", "requires Windows")
+class WindowsUtilsTest(unittest.TestCase):
+ '''Tests for the windows utils class'''
+
+ _CONFIG_NAME = 'FakeConfig'
+ _DESTINATION = '192.168.192.168'
+ _GATEWAY = '10.7.1.1'
+ _NETMASK = '255.255.255.0'
+ _PASSWORD = 'Passw0rd'
+ _SECTION = 'fake_section'
+ _USERNAME = 'Admin'
+
+ def setUp(self):
+ self._winutils = windows_utils.WindowsUtils()
+ self._conn = mock.MagicMock()
+
+ def test_enable_shutdown_privilege(self):
+ fake_process = mock.MagicMock()
+ fake_token = True
+ private_LUID = 'fakeid'
+ win32process.GetCurrentProcess = mock.MagicMock(
+ return_value=fake_process)
+ win32security.OpenProcessToken = mock.MagicMock(
+ return_value=fake_token)
+ win32security.LookupPrivilegeValue = mock.MagicMock(
+ return_value=private_LUID)
+ win32security.AdjustTokenPrivileges = mock.MagicMock()
+ self._winutils._enable_shutdown_privilege()
+ privilege = [(private_LUID, win32security.SE_PRIVILEGE_ENABLED)]
+ win32security.AdjustTokenPrivileges.assert_called_with(
+ fake_token,
+ False,
+ privilege)
+
+ win32security.OpenProcessToken.assert_called_with(
+ fake_process,
+ win32security.TOKEN_ADJUST_PRIVILEGES |
+ win32security.TOKEN_QUERY)
+
+ @mock.patch('cloudbaseinit.osutils.windows.WindowsUtils'
+ '._enable_shutdown_privilege')
+ def _test_reboot(self, mock_enable_shutdown_privilege, ret_value):
+ windll.advapi32.InitiateSystemShutdownW = mock.MagicMock(
+ return_value=ret_value)
+
+ if not ret_value:
+ self.assertRaises(Exception, self._winutils.reboot)
+ else:
+ self._winutils.reboot()
+
+ windll.advapi32.InitiateSystemShutdownW.assert_called_with(
+ 0,
+ "Cloudbase-Init reboot",
+ 0, True, True)
+
+ def test_reboot(self):
+ self._test_reboot(ret_value=True)
+
+ def test_reboot_failed(self):
+ self._test_reboot(ret_value=None)
+
+ @mock.patch('wmi.WMI')
+ @mock.patch('cloudbaseinit.osutils.windows.WindowsUtils'
+ '._sanitize_wmi_input')
+ def _test_get_user_wmi_object(self, mock_sanitize_wmi_input, mock_WMI,
+ returnvalue):
+ mock_WMI.return_value = self._conn
+ mock_sanitize_wmi_input.return_value = self._USERNAME
+ self._conn.query.return_value = returnvalue
+ response = self._winutils._get_user_wmi_object(self._USERNAME)
+ self._conn.query.assert_called_with("SELECT * FROM Win32_Account "
+ "where name = \'%s\'" %
+ self._USERNAME)
+ mock_sanitize_wmi_input.assert_called_with(self._USERNAME)
+ mock_WMI.assert_called_with(moniker='//./root/cimv2')
+ if returnvalue:
+ self.assertTrue(response is not None)
+ else:
+ self.assertTrue(response is None)
+
+ def test_get_user_wmi_object(self):
+ caption = 'fake'
+ self._test_get_user_wmi_object(returnvalue=caption)
+
+ def test_no_user_wmi_object(self):
+ empty_caption = ''
+ self._test_get_user_wmi_object(returnvalue=empty_caption)
+
+ @mock.patch('cloudbaseinit.osutils.windows.WindowsUtils'
+ '._get_user_wmi_object')
+ def _test_user_exists(self, mock_get_user_wmi_object, returnvalue):
+ mock_get_user_wmi_object.return_value = returnvalue
+ response = self._winutils.user_exists(returnvalue)
+ mock_get_user_wmi_object.assert_called_with(returnvalue)
+ if returnvalue:
+ self.assertTrue(response)
+ else:
+ self.assertFalse(response)
+
+ def test_user_exists(self):
+ self._test_user_exists(returnvalue=self._USERNAME)
+
+ def test_username_does_not_exist(self):
+ self._test_user_exists(returnvalue=None)
+
+ def test_sanitize_wmi_input(self):
+ unsanitised = ' \' '
+ response = self._winutils._sanitize_wmi_input(unsanitised)
+ sanitised = ' \'\' '
+ self.assertEqual(response, sanitised)
+
+ def test_sanitize_shell_input(self):
+ unsanitised = ' " '
+ response = self._winutils.sanitize_shell_input(unsanitised)
+ sanitised = ' \\" '
+ self.assertEqual(response, sanitised)
+
+ @mock.patch('cloudbaseinit.osutils.windows.WindowsUtils'
+ '._set_user_password_expiration')
+ @mock.patch('cloudbaseinit.osutils.windows.WindowsUtils'
+ '.execute_process')
+ def _test_create_or_change_user(self, mock_execute_process,
+ mock_set_user_password_expiration,
+ create, password_expires, ret_value=0):
+ args = ['NET', 'USER', self._USERNAME, self._PASSWORD]
+ if create:
+ args.append('/ADD')
+ mock_execute_process.return_value = (None, None, ret_value)
+ if not ret_value:
+ self._winutils._create_or_change_user(self._USERNAME,
+ self._PASSWORD, create,
+ password_expires)
+ mock_set_user_password_expiration.assert_called_with(
+ self._USERNAME, password_expires)
+ else:
+ self.assertRaises(
+ Exception, self._winutils._create_or_change_user,
+ self._USERNAME, self._PASSWORD, create, password_expires)
+ mock_execute_process.assert_called_with(args)
+
+ def test_create_user_and_add_password_expire_true(self):
+ self._test_create_or_change_user(create=True, password_expires=True)
+
+ def test_create_user_and_add_password_expire_false(self):
+ self._test_create_or_change_user(create=True, password_expires=False)
+
+ def test_add_password_expire_true(self):
+ self._test_create_or_change_user(create=False, password_expires=True)
+
+ def test_add_password_expire_false(self):
+ self._test_create_or_change_user(create=False, password_expires=False)
+
+ def test_create_user_and_add_password_expire_true_with_ret_value(self):
+ self._test_create_or_change_user(create=True, password_expires=True,
+ ret_value=1)
+
+ def test_create_user_and_add_password_expire_false_with_ret_value(self):
+ self._test_create_or_change_user(create=True,
+ password_expires=False, ret_value=1)
+
+ def test_add_password_expire_true_with_ret_value(self):
+ self._test_create_or_change_user(create=False,
+ password_expires=True, ret_value=1)
+
+ def test_add_password_expire_false_with_ret_value(self):
+ self._test_create_or_change_user(create=False,
+ password_expires=False, ret_value=1)
+
+ @mock.patch('cloudbaseinit.osutils.windows.WindowsUtils'
+ '._get_user_wmi_object')
+ def _test_set_user_password_expiration(self, mock_get_user_wmi_object,
+ fake_obj):
+ mock_get_user_wmi_object.return_value = fake_obj
+ response = self._winutils._set_user_password_expiration(
+ self._USERNAME, True)
+ if fake_obj:
+ self.assertTrue(fake_obj.PasswordExpires)
+ self.assertTrue(response)
+ else:
+ self.assertFalse(response)
+
+ def test_set_password_expiration(self):
+ fake = mock.Mock()
+ self._test_set_user_password_expiration(fake_obj=fake)
+
+ def test_set_password_expiration_no_object(self):
+ self._test_set_user_password_expiration(fake_obj=None)
+
+ def _test_get_user_sid_and_domain(self, ret_val):
+ cbSid = mock.Mock()
+ sid = mock.Mock()
+ size = 1024
+ cchReferencedDomainName = mock.Mock()
+ domainName = mock.Mock()
+ sidNameUse = mock.Mock()
+
+ ctypes.create_string_buffer = mock.MagicMock(return_value=sid)
+ ctypes.sizeof = mock.MagicMock(return_value=size)
+ wintypes.DWORD = mock.MagicMock(return_value=cchReferencedDomainName)
+ ctypes.create_unicode_buffer = mock.MagicMock(return_value=domainName)
+
+ ctypes.byref = mock.MagicMock()
+
+ windll.advapi32.LookupAccountNameW = mock.MagicMock(
+ return_value=ret_val)
+ if ret_val is None:
+ self.assertRaises(
+ Exception, self._winutils._get_user_sid_and_domain,
+ self._USERNAME)
+ else:
+ response = self._winutils._get_user_sid_and_domain(self._USERNAME)
+
+ windll.advapi32.LookupAccountNameW.assert_called_with(
+ 0, unicode(self._USERNAME), sid, ctypes.byref(cbSid),
+ domainName, ctypes.byref(cchReferencedDomainName),
+ ctypes.byref(sidNameUse))
+ self.assertEqual(response, (sid, domainName.value))
+
+ def test_get_user_sid_and_domain(self):
+ fake_obj = mock.Mock()
+ self._test_get_user_sid_and_domain(ret_val=fake_obj)
+
+ def test_get_user_sid_and_domain_no_return_value(self):
+ self._test_get_user_sid_and_domain(ret_val=None)
+
+ def _test_add_user_to_local_group(self, ret_value):
+ windows_utils.Win32_LOCALGROUP_MEMBERS_INFO_3 = mock.MagicMock()
+ lmi = windows_utils.Win32_LOCALGROUP_MEMBERS_INFO_3()
+ group_name = 'Admins'
+
+ windll.netapi32.NetLocalGroupAddMembers = mock.MagicMock(
+ return_value=ret_value)
+
+ if ret_value is not 0:
+ self.assertRaises(
+ Exception, self._winutils.add_user_to_local_group,
+ self._USERNAME, group_name)
+ else:
+ ctypes.addressof = mock.MagicMock()
+ self._winutils.add_user_to_local_group(self._USERNAME,
+ group_name)
+ windll.netapi32.NetLocalGroupAddMembers.assert_called_with(
+ 0, unicode(group_name), 3, ctypes.addressof(lmi), 1)
+ self.assertEqual(lmi.lgrmi3_domainandname, unicode(self._USERNAME))
+
+ def test_add_user_to_local_group_no_error(self):
+ self._test_add_user_to_local_group(ret_value=0)
+
+ def test_add_user_to_local_group_not_found(self):
+ self._test_add_user_to_local_group(
+ ret_value=self._winutils.NERR_GroupNotFound)
+
+ def test_add_user_to_local_group_access_denied(self):
+ self._test_add_user_to_local_group(
+ ret_value=self._winutils.ERROR_ACCESS_DENIED)
+
+ def test_add_user_to_local_group_no_member(self):
+ self._test_add_user_to_local_group(
+ ret_value=self._winutils.ERROR_NO_SUCH_MEMBER)
+
+ def test_add_user_to_local_group_member_in_alias(self):
+ self._test_add_user_to_local_group(
+ ret_value=self._winutils.ERROR_MEMBER_IN_ALIAS)
+
+ def test_add_user_to_local_group_invalid_member(self):
+ self._test_add_user_to_local_group(
+ ret_value=self._winutils.ERROR_INVALID_MEMBER)
+
+ @mock.patch('cloudbaseinit.osutils.windows.WindowsUtils'
+ '._get_user_wmi_object')
+ def _test_get_user_sid(self, mock_get_user_wmi_object, fail):
+ r = mock.Mock()
+ if not fail:
+ mock_get_user_wmi_object.return_value = None
+ response = self._winutils.get_user_sid(self._USERNAME)
+ self.assertTrue(response is None)
+ else:
+ mock_get_user_wmi_object.return_value = r
+ response = self._winutils.get_user_sid(self._USERNAME)
+ self.assertTrue(response is not None)
+ mock_get_user_wmi_object.assert_called_with(self._USERNAME)
+
+ def test_get_user_sid(self):
+ self._test_get_user_sid(fail=False)
+
+ def test_get_user_sid_fail(self):
+ self._test_get_user_sid(fail=True)
+
+ def _test_create_user_logon_session(self, logon, loaduser,
+ load_profile=True):
+ wintypes.HANDLE = mock.MagicMock()
+ pi = windows_utils.Win32_PROFILEINFO()
+ windll.advapi32.LogonUserW = mock.MagicMock(return_value=logon)
+ ctypes.byref = mock.MagicMock()
+
+ if not logon:
+ self.assertRaises(
+ Exception, self._winutils.create_user_logon_session,
+ self._USERNAME, self._PASSWORD, domain='.',
+ load_profile=load_profile)
+
+ elif load_profile and not loaduser:
+ windll.userenv.LoadUserProfileW = mock.MagicMock(
+ return_value=None)
+ windll.kernel32.CloseHandle = mock.MagicMock(return_value=None)
+ self.assertRaises(Exception,
+ self._winutils.create_user_logon_session,
+ self._USERNAME, self._PASSWORD, domain='.',
+ load_profile=load_profile)
+
+ windll.userenv.LoadUserProfileW.assert_called_with(
+ wintypes.HANDLE(), ctypes.byref(pi))
+ windll.kernel32.CloseHandle.assert_called_with(wintypes.HANDLE())
+
+ elif not load_profile:
+ response = self._winutils.create_user_logon_session(
+ self._USERNAME, self._PASSWORD, domain='.',
+ load_profile=load_profile)
+ self.assertTrue(response is not None)
+ else:
+ size = 1024
+ windll.userenv.LoadUserProfileW = mock.MagicMock()
+ ctypes.sizeof = mock.MagicMock(return_value=size)
+ windows_utils.Win32_PROFILEINFO = mock.MagicMock(
+ return_value=loaduser)
+
+ response = self._winutils.create_user_logon_session(
+ self._USERNAME, self._PASSWORD, domain='.',
+ load_profile=load_profile)
+
+ windll.userenv.LoadUserProfileW.assert_called_with(
+ wintypes.HANDLE(), ctypes.byref(pi))
+ self.assertTrue(response is not None)
+
+ def test_create_user_logon_session_fail_load_false(self):
+ self._test_create_user_logon_session(0, 0, True)
+
+ def test_create_user_logon_session_fail_load_true(self):
+ self._test_create_user_logon_session(0, 0, False)
+
+ def test_create_user_logon_session_load_true(self):
+ m = mock.Mock()
+ n = mock.Mock()
+ self._test_create_user_logon_session(m, n, True)
+
+ def test_create_user_logon_session_load_false(self):
+ m = mock.Mock()
+ n = mock.Mock()
+ self._test_create_user_logon_session(m, n, False)
+
+ def test_create_user_logon_session_no_load_true(self):
+ m = mock.Mock()
+ self._test_create_user_logon_session(m, None, True)
+
+ def test_create_user_logon_session_no_load_false(self):
+ m = mock.Mock()
+ self._test_create_user_logon_session(m, None, False)
+
+ def test_close_user_logon_session(self):
+ token = mock.Mock()
+ windll.kernel32.CloseHandle = mock.MagicMock()
+ self._winutils.close_user_logon_session(token)
+ windll.kernel32.CloseHandle.assert_called_with(token)
+
+ @mock.patch('ctypes.windll.kernel32.SetComputerNameExW')
+ def _test_set_host_name(self, mock_SetComputerNameExW, ret_value):
+ wmi.WMI = mock.MagicMock(return_value=self._conn)
+ mock_SetComputerNameExW.return_value = ret_value
+ if not ret_value:
+ self.assertRaises(Exception, self._winutils.set_host_name,
+ 'fake name')
+ else:
+ self._winutils.set_host_name('fake name')
+ mock_SetComputerNameExW.assert_called_with(
+ self._winutils.ComputerNamePhysicalDnsHostname,
+ unicode('fake name'))
+
+ def test_set_host_name(self):
+ self._test_set_host_name(ret_value='fake response')
+
+ def test_set_host_exception(self):
+ self._test_set_host_name(ret_value=None)
+
+ @mock.patch('cloudbaseinit.osutils.windows.WindowsUtils'
+ '.get_user_sid')
+ def _test_get_user_home(self, mock_get_user_sid, user_sid):
+ key = mock.MagicMock()
+ mock_get_user_sid.return_value = user_sid
+ _winreg.OpenKey = mock.MagicMock(return_value=key)
+ _winreg.QueryValueEx = mock.MagicMock()
+ response = self._winutils.get_user_home(self._USERNAME)
+ if user_sid:
+ mock_get_user_sid.assert_called_with(self._USERNAME)
+ _winreg.OpenKey.assert_called_with(
+ _winreg.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Microsoft\\Windows '
+ 'NT\\CurrentVersion\\ProfileList\\'
+ '%s' % mock_get_user_sid())
+ self.assertTrue(response is not None)
+ _winreg.QueryValueEx.assert_called_with(
+ _winreg.OpenKey().__enter__(), 'ProfileImagePath')
+ else:
+ self.assertTrue(response is None)
+
+ def test_get_user_home(self):
+ user = mock.MagicMock()
+ self._test_get_user_home(user_sid=user)
+
+ def test_get_user_home_fail(self):
+ self._test_get_user_home(user_sid=None)
+
+ @mock.patch('wmi.WMI')
+ def test_get_network_adapters(self, mock_WMI):
+ mock_WMI.return_value = self._conn
+ mock_response = mock.MagicMock()
+ self._conn.query.return_value = [mock_response]
+ response = self._winutils.get_network_adapters()
+ self._conn.query.assert_called_with(
+ 'SELECT * FROM Win32_NetworkAdapter WHERE AdapterTypeId = 0 AND '
+ 'PhysicalAdapter = True AND MACAddress IS NOT NULL')
+ self.assertEqual(response, [mock_response.Name])
+
+ @mock.patch('cloudbaseinit.osutils.windows.WindowsUtils'
+ '._sanitize_wmi_input')
+ def _test_set_static_network_config(self, mock_sanitize_wmi_input,
+ adapter, ret_val1=None,
+ ret_val2=None, ret_val3=None):
+ wmi.WMI = mock.MagicMock(return_value=self._conn)
+ address = '10.10.10.10'
+ adapter_name = 'adapter_name'
+ broadcast = '0.0.0.0'
+ dns_list = ['8.8.8.8']
+
+ if not adapter:
+ self.assertRaises(
+ Exception, self._winutils.set_static_network_config,
+ adapter_name, address, self._NETMASK,
+ broadcast, self._GATEWAY, dns_list)
+ else:
+ mock_sanitize_wmi_input.return_value = adapter_name
+ self._conn.query.return_value = adapter
+ adapter_config = adapter[0].associators()[0]
+ adapter_config.EnableStatic = mock.MagicMock(return_value=ret_val1)
+ adapter_config.SetGateways = mock.MagicMock(return_value=ret_val2)
+ adapter_config.SetDNSServerSearchOrder = mock.MagicMock(
+ return_value=ret_val3)
+ adapter.__len__ = mock.MagicMock(return_value=1)
+ if ret_val1[0] > 1:
+ self.assertRaises(
+ Exception, self._winutils.set_static_network_config,
+ adapter_name, address, self._NETMASK,
+ broadcast, self._GATEWAY, dns_list)
+
+ elif ret_val2[0] > 1:
+ self.assertRaises(
+ Exception, self._winutils.set_static_network_config,
+ adapter_name, address, self._NETMASK,
+ broadcast, self._GATEWAY, dns_list)
+
+ elif ret_val3[0] > 1:
+ self.assertRaises(
+ Exception, self._winutils.set_static_network_config,
+ adapter_name, address, self._NETMASK,
+ broadcast, self._GATEWAY, dns_list)
+
+ else:
+ response = self._winutils.set_static_network_config(
+ adapter_name, address, self._NETMASK,
+ broadcast, self._GATEWAY, dns_list)
+ if ret_val1[0] or ret_val2[0] or ret_val3[0] == 1:
+ self.assertTrue(response)
+ else:
+ self.assertFalse(response)
+ adapter_config.EnableStatic.assert_called_with(
+ [address], [self._NETMASK])
+ adapter_config.SetGateways.assert_called_with(
+ [self._GATEWAY], [1])
+ adapter_config.SetDNSServerSearchOrder.assert_called_with(
+ dns_list)
+
+ self._winutils._sanitize_wmi_input.assert_called_with(
+ adapter_name)
+ adapter[0].associators.assert_called_with(
+ wmi_result_class='Win32_NetworkAdapterConfiguration')
+ self._conn.query.assert_called_with(
+ 'SELECT * FROM Win32_NetworkAdapter WHERE MACAddress IS '
+ 'NOT NULL AND Name = \'%(adapter_name_san)s\'' %
+ {'adapter_name_san': adapter_name})
+
+ def test_set_static_network_config(self):
+ adapter = mock.MagicMock()
+ ret_val1 = (1,)
+ ret_val2 = (1,)
+ ret_val3 = (0,)
+ self._test_set_static_network_config(adapter=adapter,
+ ret_val1=ret_val1,
+ ret_val2=ret_val2,
+ ret_val3=ret_val3)
+
+ def test_set_static_network_config_query_fail(self):
+ self._test_set_static_network_config(adapter=None)
+
+ def test_set_static_network_config_cannot_set_ip(self):
+ adapter = mock.MagicMock()
+ ret_val1 = (2,)
+ self._test_set_static_network_config(adapter=adapter,
+ ret_val1=ret_val1)
+
+ def test_set_static_network_config_cannot_set_gateway(self):
+ adapter = mock.MagicMock()
+ ret_val1 = (1,)
+ ret_val2 = (2,)
+ self._test_set_static_network_config(adapter=adapter,
+ ret_val1=ret_val1,
+ ret_val2=ret_val2)
+
+ def test_set_static_network_config_cannot_set_DNS(self):
+ adapter = mock.MagicMock()
+ ret_val1 = (1,)
+ ret_val2 = (1,)
+ ret_val3 = (2,)
+ self._test_set_static_network_config(adapter=adapter,
+ ret_val1=ret_val1,
+ ret_val2=ret_val2,
+ ret_val3=ret_val3)
+
+ def test_set_static_network_config_no_reboot(self):
+ adapter = mock.MagicMock()
+ ret_val1 = (0,)
+ ret_val2 = (0,)
+ ret_val3 = (0,)
+ self._test_set_static_network_config(adapter=adapter,
+ ret_val1=ret_val1,
+ ret_val2=ret_val2,
+ ret_val3=ret_val3)
+
+ def _test_get_config_key_name(self, section):
+ response = self._winutils._get_config_key_name(section)
+ if section:
+ self.assertEqual(
+ response, self._winutils._config_key + section + '\\')
+ else:
+ self.assertEqual(response, self._winutils._config_key)
+
+ def test_get_config_key_name_with_section(self):
+ self._test_get_config_key_name(self._SECTION)
+
+ def test_get_config_key_name_no_section(self):
+ self._test_get_config_key_name(None)
+
+ @mock.patch('cloudbaseinit.osutils.windows.WindowsUtils'
+ '._get_config_key_name')
+ def _test_set_config_value(self, mock_get_config_key_name, value):
+ key = mock.MagicMock()
+ key_name = self._winutils._config_key + self._SECTION + '\\' + self\
+ ._CONFIG_NAME
+ mock_get_config_key_name.return_value = key_name
+
+ _winreg.CreateKey = mock.MagicMock()
+ _winreg.REG_DWORD = mock.Mock()
+ _winreg.REG_SZ = mock.Mock()
+ _winreg.SetValueEx = mock.MagicMock()
+
+ self._winutils.set_config_value(self._CONFIG_NAME, value,
+ self._SECTION)
+
+ _winreg.CreateKey.__enter__.return_value = key
+ with _winreg.CreateKey as m:
+ assert m == key
+
+ _winreg.CreateKey.__enter__.assert_called_with()
+ _winreg.CreateKey.__exit__.assert_called_with(None, None, None)
+ _winreg.CreateKey.assert_called_with(_winreg.HKEY_LOCAL_MACHINE,
+ key_name)
+ mock_get_config_key_name.assert_called_with(self._SECTION)
+ if type(value) == int:
+ _winreg.SetValueEx.assert_called_with(
+ _winreg.CreateKey().__enter__(), self._CONFIG_NAME, 0,
+ _winreg.REG_DWORD, value)
+ else:
+ _winreg.SetValueEx.assert_called_with(
+ _winreg.CreateKey().__enter__(), self._CONFIG_NAME, 0,
+ _winreg.REG_SZ, value)
+
+ def test_set_config_value_int(self):
+ self._test_set_config_value(value=1)
+
+ def test_set_config_value_not_int(self):
+ self._test_set_config_value(value='1')
+
+ @mock.patch('cloudbaseinit.osutils.windows.WindowsUtils'
+ '._get_config_key_name')
+ def _test_get_config_value(self, mock_get_config_key_name, value):
+ key_name = self._winutils._config_key + self._SECTION + '\\'
+ key_name += self._CONFIG_NAME
+ _winreg.OpenKey = mock.MagicMock()
+ _winreg.REG_DWORD = mock.Mock()
+ _winreg.REG_SZ = mock.Mock()
+ if type(value) == int:
+ regtype = _winreg.REG_DWORD
+ else:
+ regtype = _winreg.REG_SZ
+ _winreg.QueryValueEx = mock.MagicMock(return_value=(value, regtype))
+ if value is None:
+ mock_get_config_key_name.side_effect = [WindowsError]
+ self.assertRaises(WindowsError, self._winutils.get_config_value,
+ self._CONFIG_NAME, None)
+ else:
+ mock_get_config_key_name.return_value = key_name
+ response = self._winutils.get_config_value(self._CONFIG_NAME,
+ self._SECTION)
+ _winreg.OpenKey.assert_called_with(_winreg.HKEY_LOCAL_MACHINE,
+ key_name)
+ mock_get_config_key_name.assert_called_with(self._SECTION)
+ _winreg.QueryValueEx.assert_called_with(
+ _winreg.OpenKey().__enter__(), self._CONFIG_NAME)
+ self.assertEqual(response, value)
+
+ def test_get_config_value_type_int(self):
+ self._test_get_config_value(value=1)
+
+ def test_get_config_value_type_str(self):
+ self._test_get_config_value(value='fake')
+
+ def test_get_config_value_type_error(self):
+ self._test_get_config_value(value=None)
+
+ def _test_wait_for_boot_completion(self, ret_val):
+ key = mock.MagicMock()
+ time.sleep = mock.MagicMock()
+ _winreg.OpenKey = mock.MagicMock()
+ _winreg.QueryValueEx = mock.MagicMock()
+ _winreg.QueryValueEx.side_effect = ret_val
+ self._winutils.wait_for_boot_completion()
+ _winreg.OpenKey.__enter__.return_value = key
+ _winreg.OpenKey.assert_called_with(
+ _winreg.HKEY_LOCAL_MACHINE,
+ "SYSTEM\\Setup\\Status\\SysprepStatus", 0, _winreg.KEY_READ)
+
+ _winreg.QueryValueEx.assert_called_with(
+ _winreg.OpenKey().__enter__(), "GeneralizationState")
+
+ def test_wait_for_boot_completion(self):
+ ret_val = [[7]]
+ self._test_wait_for_boot_completion(ret_val)
+
+ @mock.patch('wmi.WMI')
+ def test_get_service(self, mock_WMI):
+ mock_WMI.return_value = self._conn
+ self._conn.Win32_Service.return_value = ['fake name']
+ response = self._winutils._get_service('fake name')
+ mock_WMI.assert_called_with(moniker='//./root/cimv2')
+ self._conn.Win32_Service.assert_called_with(Name='fake name')
+ self.assertEqual(response, 'fake name')
+
+ @mock.patch('cloudbaseinit.osutils.windows.WindowsUtils'
+ '._get_service')
+ def test_check_service_exists(self, mock_get_service):
+ mock_get_service.return_value = 'not None'
+ response = self._winutils.check_service_exists('fake name')
+ self.assertEqual(response, True)
+
+ @mock.patch('cloudbaseinit.osutils.windows.WindowsUtils'
+ '._get_service')
+ def test_get_service_status(self, mock_get_service):
+ mock_service = mock.MagicMock()
+ mock_get_service.return_value = mock_service
+ response = self._winutils.get_service_status('fake name')
+ self.assertEqual(response, mock_service.State)
+
+ @mock.patch('cloudbaseinit.osutils.windows.WindowsUtils'
+ '._get_service')
+ def test_get_service_start_mode(self, mock_get_service):
+ mock_service = mock.MagicMock()
+ mock_get_service.return_value = mock_service
+ response = self._winutils.get_service_start_mode('fake name')
+ self.assertEqual(response, mock_service.StartMode)
+
+ @mock.patch('cloudbaseinit.osutils.windows.WindowsUtils'
+ '._get_service')
+ def _test_set_service_start_mode(self, mock_get_service, ret_val):
+ mock_service = mock.MagicMock()
+ mock_get_service.return_value = mock_service
+ mock_service.ChangeStartMode.return_value = (ret_val,)
+ if ret_val != 0:
+ self.assertRaises(Exception,
+ self._winutils.set_service_start_mode,
+ 'fake name', 'fake mode')
+ else:
+ self._winutils.set_service_start_mode('fake name', 'fake mode')
+ mock_service.ChangeStartMode.assert_called_once_with('fake mode')
+
+ def test_set_service_start_mode(self):
+ self._test_set_service_start_mode(ret_val=0)
+
+ def test_set_service_start_mode_exception(self):
+ self._test_set_service_start_mode(ret_val=1)
+
+ @mock.patch('cloudbaseinit.osutils.windows.WindowsUtils'
+ '._get_service')
+ def _test_start_service(self, mock_get_service, ret_val):
+ mock_service = mock.MagicMock()
+ mock_get_service.return_value = mock_service
+ mock_service.StartService.return_value = (ret_val,)
+ if ret_val != 0:
+ self.assertRaises(Exception,
+ self._winutils.start_service,
+ 'fake name')
+ else:
+ self._winutils.start_service('fake name')
+ mock_service.StartService.assert_called_once_with()
+
+ def test_start_service(self):
+ self._test_set_service_start_mode(ret_val=0)
+
+ def test_start_service_exception(self):
+ self._test_set_service_start_mode(ret_val=1)
+
+ @mock.patch('cloudbaseinit.osutils.windows.WindowsUtils'
+ '._get_service')
+ def _test_stop_service(self, mock_get_service, ret_val):
+ mock_service = mock.MagicMock()
+ mock_get_service.return_value = mock_service
+ mock_service.StopService.return_value = (ret_val,)
+ if ret_val != 0:
+ self.assertRaises(Exception,
+ self._winutils.stop_service,
+ 'fake name')
+ else:
+ self._winutils.stop_service('fake name')
+ mock_service.StopService.assert_called_once_with()
+
+ def test_stop_service(self):
+ self._test_stop_service(ret_val=0)
+
+ def test_stop_service_exception(self):
+ self._test_stop_service(ret_val=1)
+
+ @mock.patch('cloudbaseinit.osutils.windows.WindowsUtils'
+ '.stop_service')
+ def test_terminate(self, mock_stop_service):
+ time.sleep = mock.MagicMock()
+ self._winutils.terminate()
+ mock_stop_service.assert_called_with(self._winutils._service_name)
+ time.sleep.assert_called_with(3)
+
+ @mock.patch('cloudbaseinit.osutils.windows.WindowsUtils'
+ '._get_ipv4_routing_table')
+ def _test_get_default_gateway(self, mock_get_ipv4_routing_table,
+ routing_table):
+ mock_get_ipv4_routing_table.return_value = [routing_table]
+ response = self._winutils.get_default_gateway()
+ mock_get_ipv4_routing_table.assert_called_once_with()
+ if routing_table[0] == '0.0.0.0':
+ self.assertEqual(response, (routing_table[3], routing_table[2]))
+ else:
+ self.assertEqual(response, (None, None))
+
+ def test_get_default_gateway(self):
+ routing_table = ['0.0.0.0', '1.1.1.1', self._GATEWAY, '8.8.8.8']
+ self._test_get_default_gateway(routing_table=routing_table)
+
+ def test_get_default_gateway_error(self):
+ routing_table = ['1.1.1.1', '1.1.1.1', self._GATEWAY, '8.8.8.8']
+ self._test_get_default_gateway(routing_table=routing_table)
+
+ @mock.patch('cloudbaseinit.osutils.windows.WindowsUtils'
+ '._get_ipv4_routing_table')
+ def _test_check_static_route_exists(self, mock_get_ipv4_routing_table,
+ routing_table):
+ mock_get_ipv4_routing_table.return_value = [routing_table]
+ response = self._winutils.check_static_route_exists(self._DESTINATION)
+ mock_get_ipv4_routing_table.assert_called_once_with()
+ if routing_table[0] == self._DESTINATION:
+ self.assertTrue(response)
+ else:
+ self.assertFalse(response)
+
+ def test_check_static_route_exists_true(self):
+ routing_table = [self._DESTINATION, '1.1.1.1', self._GATEWAY,
+ '8.8.8.8']
+ self._test_check_static_route_exists(routing_table=routing_table)
+
+ def test_check_static_route_exists_false(self):
+ routing_table = ['0.0.0.0', '1.1.1.1', self._GATEWAY, '8.8.8.8']
+ self._test_check_static_route_exists(routing_table=routing_table)
+
+ @mock.patch('cloudbaseinit.osutils.windows.WindowsUtils'
+ '.execute_process')
+ def _test_add_static_route(self, mock_execute_process, err):
+ next_hop = '10.10.10.10'
+ interface_index = 1
+ metric = 9
+ args = ['ROUTE', 'ADD', self._DESTINATION, 'MASK', self._NETMASK,
+ next_hop]
+ mock_execute_process.return_value = (None, err, None)
+ if err:
+ self.assertRaises(Exception, self._winutils.add_static_route,
+ self._DESTINATION, self._NETMASK, next_hop,
+ interface_index, metric)
+ else:
+ self._winutils.add_static_route(self._DESTINATION, self._NETMASK,
+ next_hop, interface_index, metric)
+ mock_execute_process.assert_called_with(args)
+
+ def test_add_static_route(self):
+ self._test_add_static_route(err=404)
+
+ def test_add_static_route_fail(self):
+ self._test_add_static_route(err=None)
+
+ @mock.patch('ctypes.sizeof')
+ @mock.patch('ctypes.byref')
+ @mock.patch('ctypes.windll.kernel32.VerSetConditionMask')
+ @mock.patch('ctypes.windll.kernel32.VerifyVersionInfoW')
+ @mock.patch('ctypes.windll.kernel32.GetLastError')
+ def _test_check_os_version(self, mock_GetLastError,
+ mock_VerifyVersionInfoW,
+ mock_VerSetConditionMask, mock_byref,
+ mock_sizeof, ret_value, error_value=None):
+ mock_VerSetConditionMask.return_value = 2
+ mock_VerifyVersionInfoW.return_value = ret_value
+ mock_GetLastError.return_value = error_value
+ old_version = self._winutils.ERROR_OLD_WIN_VERSION
+ if error_value and error_value is not old_version:
+ self.assertRaises(Exception, self._winutils.check_os_version, 3,
+ 1, 2)
+ mock_GetLastError.assert_called_once_with()
+ else:
+ response = self._winutils.check_os_version(3, 1, 2)
+ mock_sizeof.assert_called_once_with(
+ windows_utils.Win32_OSVERSIONINFOEX_W)
+ self.assertEqual(mock_VerSetConditionMask.call_count, 3)
+ mock_VerifyVersionInfoW.assert_called_with(mock_byref(),
+ 1 | 2 | 3 | 7,
+ 2)
+ if error_value is old_version:
+ mock_GetLastError.assert_called_with()
+ self.assertEqual(response, False)
+ else:
+ self.assertEqual(response, True)
+
+ def test_check_os_version(self):
+ m = mock.MagicMock()
+ self._test_check_os_version(ret_value=m)
+
+ def test_check_os_version_expect_False(self):
+ self._test_check_os_version(
+ ret_value=None, error_value=self._winutils.ERROR_OLD_WIN_VERSION)
+
+ def test_check_os_version_exception(self):
+ self._test_check_os_version(ret_value=None, error_value=9999)
+
+ def _test_get_volume_label(self, ret_val):
+ label = mock.MagicMock()
+ max_label_size = 261
+ drive = 'Fake_drive'
+ ctypes.create_unicode_buffer = mock.MagicMock(return_value=label)
+ ctypes.windll.kernel32.GetVolumeInformationW = mock.MagicMock(
+ return_value=ret_val)
+ response = self._winutils.get_volume_label(drive)
+ if ret_val:
+ self.assertTrue(response is not None)
+ else:
+ self.assertTrue(response is None)
+
+ ctypes.create_unicode_buffer.assert_called_with(max_label_size)
+ ctypes.windll.kernel32.GetVolumeInformationW.assert_called_with(
+ drive, label, max_label_size, 0, 0, 0, 0, 0)
+
+ def test_get_volume_label(self):
+ self._test_get_volume_label('ret')
+
+ def test_get_volume_label_no_return_value(self):
+ self._test_get_volume_label(None)
+
+ @mock.patch('re.search')
+ @mock.patch('cloudbaseinit.osutils.base.BaseOSUtils.'
+ 'generate_random_password')
+ def test_generate_random_password(self, mock_generate_random_password,
+ mock_search):
+ length = 14
+ mock_search.return_value = True
+ mock_generate_random_password.return_value = 'Passw0rd'
+ response = self._winutils.generate_random_password(length)
+ mock_generate_random_password.assert_called_once_with(length)
+ self.assertEqual(response, 'Passw0rd')
+
+ @mock.patch('ctypes.create_unicode_buffer')
+ @mock.patch('ctypes.windll.kernel32.GetLogicalDriveStringsW')
+ def _test_get_logical_drives(self, mock_GetLogicalDriveStringsW,
+ mock_create_unicode_buffer, buf_length):
+ mock_buf = mock.MagicMock()
+ mock_buf.__getitem__.side_effect = ['1', '\x00']
+ mock_create_unicode_buffer.return_value = mock_buf
+ mock_GetLogicalDriveStringsW.return_value = buf_length
+ if buf_length is None:
+ self.assertRaises(Exception, self._winutils._get_logical_drives)
+ else:
+ response = self._winutils._get_logical_drives()
+ print mock_buf.mock_calls
+ mock_create_unicode_buffer.assert_called_with(261)
+ mock_GetLogicalDriveStringsW.assert_called_with(260, mock_buf)
+ self.assertEqual(response, ['1'])
+
+ def test_get_logical_drives_exception(self):
+ self._test_get_logical_drives(buf_length=None)
+
+ def test_get_logical_drives(self):
+ self._test_get_logical_drives(buf_length=2)
+
+ @mock.patch('cloudbaseinit.osutils.windows.WindowsUtils.'
+ '_get_logical_drives')
+ @mock.patch('cloudbaseinit.osutils.windows.kernel32')
+ def test_get_cdrom_drives(self, mock_kernel32, mock_get_logical_drives):
+ mock_get_logical_drives.return_value = ['drive']
+ mock_kernel32.GetDriveTypeW.return_value = self._winutils.DRIVE_CDROM
+ response = self._winutils.get_cdrom_drives()
+ mock_get_logical_drives.assert_called_with()
+ self.assertEqual(response, ['drive'])
+
+ @mock.patch('win32com.client.Dispatch')
+ @mock.patch('cloudbaseinit.osutils.windows.WindowsUtils._get_fw_protocol')
+ def _test_firewall_create_rule(self, mock_get_fw_protocol, mock_Dispatch):
+ self._winutils.firewall_create_rule(
+ name='fake name', port=9999, protocol=self._winutils.PROTOCOL_TCP)
+ expected = [mock.call("HNetCfg.FWOpenPort"),
+ mock.call("HNetCfg.FwMgr")]
+ self.assertEqual(mock_Dispatch.call_args_list, expected)
+ mock_get_fw_protocol.assert_called_once_with(
+ self._winutils.PROTOCOL_TCP)
+
+ @mock.patch('win32com.client.Dispatch')
+ @mock.patch('cloudbaseinit.osutils.windows.WindowsUtils._get_fw_protocol')
+ def test_firewall_remove_rule(self, mock_get_fw_protocol, mock_Dispatch):
+ self._winutils.firewall_remove_rule(
+ name='fake name', port=9999, protocol=self._winutils.PROTOCOL_TCP)
+ mock_Dispatch.assert_called_once_with("HNetCfg.FwMgr")
+ mock_get_fw_protocol.assert_called_once_with(
+ self._winutils.PROTOCOL_TCP)
diff --git a/cloudbaseinit/tests/plugins/__init__.py b/cloudbaseinit/tests/plugins/__init__.py
new file mode 100644
index 00000000..7227b295
--- /dev/null
+++ b/cloudbaseinit/tests/plugins/__init__.py
@@ -0,0 +1,15 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 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.
diff --git a/cloudbaseinit/tests/plugins/test_factory.py b/cloudbaseinit/tests/plugins/test_factory.py
new file mode 100644
index 00000000..f45b3bc0
--- /dev/null
+++ b/cloudbaseinit/tests/plugins/test_factory.py
@@ -0,0 +1,37 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 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 mock
+import unittest
+
+from cloudbaseinit.plugins import factory
+from cloudbaseinit.openstack.common import cfg
+
+CONF = cfg.CONF
+
+
+class PluginFactoryTests(unittest.TestCase):
+ def setUp(self):
+ self._factory = factory.PluginFactory()
+
+ @mock.patch('cloudbaseinit.utils.classloader.ClassLoader.load_class')
+ def test_load_plugins(self, mock_load_class):
+ expected = []
+ for path in CONF.plugins:
+ expected.append(mock.call(path))
+ response = self._factory.load_plugins()
+ self.assertEqual(mock_load_class.call_args_list, expected)
+ self.assertTrue(response is not None)
diff --git a/cloudbaseinit/tests/plugins/windows/__init__.py b/cloudbaseinit/tests/plugins/windows/__init__.py
new file mode 100644
index 00000000..7227b295
--- /dev/null
+++ b/cloudbaseinit/tests/plugins/windows/__init__.py
@@ -0,0 +1,15 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 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.
diff --git a/cloudbaseinit/tests/plugins/windows/test_createuser.py b/cloudbaseinit/tests/plugins/windows/test_createuser.py
new file mode 100644
index 00000000..8ff6699f
--- /dev/null
+++ b/cloudbaseinit/tests/plugins/windows/test_createuser.py
@@ -0,0 +1,77 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 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 mock
+import unittest
+
+from cloudbaseinit.openstack.common import cfg
+from cloudbaseinit.plugins import base
+from cloudbaseinit.plugins.windows import createuser
+
+CONF = cfg.CONF
+
+
+class CreateUserPluginTests(unittest.TestCase):
+
+ def setUp(self):
+ self._create_user = createuser.CreateUserPlugin()
+
+ def test_get_password(self):
+ mock_osutils = mock.MagicMock()
+ mock_osutils.generate_random_password.return_value = 'fake password'
+ response = self._create_user._get_password(mock_osutils)
+ mock_osutils.generate_random_password.assert_called_once_with(14)
+ self.assertEqual(response, 'fake password')
+
+ @mock.patch('cloudbaseinit.osutils.factory.OSUtilsFactory.get_os_utils')
+ @mock.patch('cloudbaseinit.plugins.windows.createuser.CreateUserPlugin'
+ '._get_password')
+ def _test_execute(self, mock_get_password, mock_get_os_utils,
+ user_exists=True):
+ CONF.set_override('groups', ['Admins'])
+ shared_data = {}
+ mock_token = mock.MagicMock()
+ mock_osutils = mock.MagicMock()
+ mock_service = mock.MagicMock()
+ mock_get_password.return_value = 'password'
+ mock_get_os_utils.return_value = mock_osutils
+ mock_osutils.user_exists.return_value = user_exists
+ mock_osutils.create_user_logon_session.return_value = mock_token
+
+ response = self._create_user.execute(mock_service, shared_data)
+
+ mock_get_os_utils.assert_called_once_with()
+ mock_get_password.assert_called_once_with(mock_osutils)
+ mock_osutils.user_exists.assert_called_once_with(CONF.username)
+ if user_exists:
+ mock_osutils.set_user_password.assert_called_once_with(
+ CONF.username, 'password')
+ else:
+ mock_osutils.create_user.assert_called_once_with(CONF.username,
+ 'password')
+ mock_osutils.create_user_logon_session.assert_called_once_with(
+ CONF.username, 'password', True)
+ mock_osutils.close_user_logon_session.assert_called_once_with(
+ mock_token)
+ mock_osutils.add_user_to_local_group.assert_called_once_with(
+ CONF.username, CONF.groups[0])
+ self.assertEqual(response, (base.PLUGIN_EXECUTION_DONE, False))
+
+ def test_execute_user_exists(self):
+ self._test_execute(user_exists=True)
+
+ def test_execute_no_user(self):
+ self._test_execute(user_exists=False)
diff --git a/cloudbaseinit/tests/plugins/windows/test_extendvolumes.py b/cloudbaseinit/tests/plugins/windows/test_extendvolumes.py
new file mode 100644
index 00000000..16334bef
--- /dev/null
+++ b/cloudbaseinit/tests/plugins/windows/test_extendvolumes.py
@@ -0,0 +1,210 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright (c) 2013 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 importlib
+import mock
+import re
+import sys
+import unittest
+
+from cloudbaseinit.openstack.common import cfg
+
+CONF = cfg.CONF
+
+_ctypes_mock = mock.MagicMock()
+_comtypes_mock = mock.MagicMock()
+
+
+class ExtendVolumesPluginTests(unittest.TestCase):
+ @mock.patch.dict(sys.modules, {'comtypes': _comtypes_mock,
+ 'ctypes': _ctypes_mock})
+ def setUp(self):
+ extendvolumes = importlib.import_module('cloudbaseinit.plugins.'
+ 'windows.extendvolumes')
+ self._extend_volumes = extendvolumes.ExtendVolumesPlugin()
+
+ def tearDown(self):
+ reload(sys)
+
+ @mock.patch('cloudbaseinit.plugins.windows.extendvolumes'
+ '.ExtendVolumesPlugin._get_volume_index')
+ @mock.patch('cloudbaseinit.plugins.windows.extendvolumes'
+ '.ExtendVolumesPlugin._extend_volume')
+ @mock.patch('cloudbaseinit.plugins.windows.vds.IVdsVolume')
+ def test_extend_volumes(self, _vds_mock, mock_extend_volume,
+ mock_get_volume_index):
+ mock_pack = mock.MagicMock()
+ mock_volume_idxs = mock.MagicMock()
+ mock_enum = mock.MagicMock()
+ mock_unk = mock.MagicMock()
+ mock_c = mock.MagicMock()
+ mock_volume = mock.MagicMock()
+ mock_properties = mock.MagicMock()
+ mock_pack.QueryVolumes.return_value = mock_enum
+ mock_enum.Next.side_effect = [(mock_unk, mock_c), (None, None)]
+ mock_unk.QueryInterface.return_value = mock_volume
+ mock_volume.GetProperties.return_value = mock_properties
+ _ctypes_mock.wstring_at.return_value = 'fake name'
+ mock_get_volume_index.return_value = mock_volume_idxs
+ self._extend_volumes._extend_volumes(mock_pack, [mock_volume_idxs])
+ mock_pack.QueryVolumes.assert_called_once_with()
+ mock_enum.Next.assert_called_with(1)
+ mock_unk.QueryInterface.assert_called_once_with(_vds_mock)
+ mock_volume.GetProperties.assert_called_once_with()
+ _ctypes_mock.wstring_at.assert_called_with(mock_properties.pwszName)
+ mock_get_volume_index.assert_called_once_with('fake name')
+ mock_extend_volume.assert_called_once_with(mock_pack, mock_volume,
+ mock_properties)
+ _ctypes_mock.windll.ole32.CoTaskMemFree.assert_called_once_with(
+ mock_properties.pwszName)
+
+ def test_get_volume_index(self):
+ mock_value = mock.MagicMock()
+ re.match = mock.MagicMock(return_value=mock_value)
+ mock_value.group.return_value = '9999'
+ response = self._extend_volumes._get_volume_index('$2')
+ mock_value.group.assert_called_once_with(1)
+ self.assertTrue(response == 9999)
+
+ @mock.patch('cloudbaseinit.plugins.windows.extendvolumes'
+ '.ExtendVolumesPlugin._get_volume_extents_to_resize')
+ @mock.patch('cloudbaseinit.plugins.windows.vds.VDS_INPUT_DISK')
+ def test_extend_volume(self, mock_VDS_INPUT_DISK,
+ mock_get_volume_extents_to_resize):
+ mock_disk = mock.MagicMock()
+ mock_pack = mock.MagicMock()
+ mock_volume = mock.MagicMock()
+ mock_properties = mock.MagicMock()
+ mock_volume_extent = mock.MagicMock()
+ mock_async = mock.MagicMock()
+ mock_get_volume_extents_to_resize.return_value = [(mock_volume_extent,
+ 9999)]
+ mock_VDS_INPUT_DISK.return_value = mock_disk
+ mock_volume.Extend.return_value = mock_async
+
+ self._extend_volumes._extend_volume(mock_pack, mock_volume,
+ mock_properties)
+
+ mock_get_volume_extents_to_resize.assert_called_once_with(
+ mock_pack, mock_properties.id)
+ _ctypes_mock.wstring_at.assert_called_with(mock_properties.pwszName)
+ mock_volume.Extend.assert_called_once_with(
+ mock_VDS_INPUT_DISK.__mul__()(), 1)
+ mock_async.Wait.assert_called_once_with()
+
+ @mock.patch('cloudbaseinit.plugins.windows.vds.IVdsDisk')
+ @mock.patch('cloudbaseinit.plugins.windows.vds.VDS_DISK_EXTENT')
+ def test_get_volume_extents_to_resize(self, mock_VDS_DISK_EXTENT,
+ mock_IVdsDisk):
+ mock_pack = mock.MagicMock()
+ mock_extents_p = mock.MagicMock()
+ mock_unk = mock.MagicMock()
+ mock_c = mock.MagicMock()
+ mock_disk = mock.MagicMock()
+ mock_enum = mock.MagicMock()
+ fake_volume_id = '$1'
+ mock_array = mock.MagicMock()
+ mock_array.volumeId = fake_volume_id
+ mock_pack.QueryDisks.return_value = mock_enum
+ mock_enum.Next.side_effect = [(mock_unk, mock_c), (None, None)]
+ mock_unk.QueryInterface.return_value = mock_disk
+ mock_disk.QueryExtents.return_value = (mock_extents_p,
+ 1)
+ mock_VDS_DISK_EXTENT.__mul__().from_address.return_value = [mock_array]
+
+ response = self._extend_volumes._get_volume_extents_to_resize(
+ mock_pack, fake_volume_id)
+
+ mock_pack.QueryDisks.assert_called_once_with()
+ mock_enum.Next.assert_called_with(1)
+ mock_unk.QueryInterface.assert_called_once_with(mock_IVdsDisk)
+ _ctypes_mock.addressof.assert_called_with(mock_extents_p.contents)
+ mock_VDS_DISK_EXTENT.__mul__().from_address.assert_called_with(
+ _ctypes_mock.addressof(mock_extents_p.contents))
+
+ _ctypes_mock.pointer.assert_called_once_with(
+ mock_VDS_DISK_EXTENT())
+ self.assertEqual(response, [])
+
+ _ctypes_mock.windll.ole32.CoTaskMemFree.assert_called_with(
+ mock_extents_p)
+
+ @mock.patch('cloudbaseinit.plugins.windows.vds.'
+ 'VDS_QUERY_SOFTWARE_PROVIDERS')
+ @mock.patch('cloudbaseinit.plugins.windows.vds.IVdsSwProvider')
+ def test_query_providers(self, mock_IVdsSwProvider,
+ mock_VDS_QUERY_SOFTWARE_PROVIDERS):
+ mock_svc = mock.MagicMock()
+ mock_enum = mock.MagicMock()
+ mock_unk = mock.MagicMock()
+ mock_c = mock.MagicMock()
+ mock_svc.QueryProviders.return_value = mock_enum
+ mock_enum.Next.side_effect = [(mock_unk, mock_c), (None, None)]
+ mock_unk.QueryInterface.return_value = 'fake providers'
+
+ response = self._extend_volumes._query_providers(mock_svc)
+ mock_svc.QueryProviders.assert_called_once_with(
+ mock_VDS_QUERY_SOFTWARE_PROVIDERS)
+ mock_enum.Next.assert_called_with(1)
+ mock_unk.QueryInterface.assert_called_once_with(mock_IVdsSwProvider)
+ self.assertEqual(response, ['fake providers'])
+
+ @mock.patch('cloudbaseinit.plugins.windows.vds.IVdsPack')
+ def test_query_packs(self, mock_IVdsPack):
+ mock_provider = mock.MagicMock()
+ mock_enum = mock.MagicMock()
+ mock_unk = mock.MagicMock()
+ mock_c = mock.MagicMock()
+ mock_provider.QueryPacks.return_value = mock_enum
+ mock_enum.Next.side_effect = [(mock_unk, mock_c), (None, None)]
+ mock_unk.QueryInterface.return_value = 'fake packs'
+
+ response = self._extend_volumes._query_packs(mock_provider)
+
+ mock_provider.QueryPacks.assert_called_once_with()
+ mock_enum.Next.assert_called_with(1)
+ mock_unk.QueryInterface.assert_called_once_with(mock_IVdsPack)
+ self.assertEqual(response, ['fake packs'])
+
+ def test_get_volumes_to_extend(self):
+ CONF.set_override('volumes_to_extend', '1')
+ response = self._extend_volumes._get_volumes_to_extend()
+ self.assertEqual(response, [1])
+
+ @mock.patch('cloudbaseinit.plugins.windows.vds.load_vds_service')
+ @mock.patch('cloudbaseinit.plugins.windows.extendvolumes.'
+ 'ExtendVolumesPlugin._query_providers')
+ @mock.patch('cloudbaseinit.plugins.windows.extendvolumes.'
+ 'ExtendVolumesPlugin._query_packs')
+ @mock.patch('cloudbaseinit.plugins.windows.extendvolumes.'
+ 'ExtendVolumesPlugin._extend_volumes')
+ def test_execute(self, mock_extend_volumes, mock_query_packs,
+ mock_query_providers, mock_load_vds_service):
+ CONF.set_override('volumes_to_extend', '1')
+ mock_svc = mock.MagicMock()
+ fake_providers = ['fake providers']
+ fake_packs = ['fake packs']
+ mock_service = mock.MagicMock()
+ fake_data = 'fake data'
+ mock_load_vds_service.return_value = mock_svc
+ mock_query_providers.return_value = fake_providers
+ mock_query_packs.return_value = fake_packs
+
+ self._extend_volumes.execute(mock_service, fake_data)
+
+ mock_query_providers.assert_called_once_with(mock_svc)
+ mock_query_packs.assert_called_once_with('fake providers')
+ mock_extend_volumes.assert_called_with('fake packs', [1])
diff --git a/cloudbaseinit/tests/plugins/windows/test_networkconfig.py b/cloudbaseinit/tests/plugins/windows/test_networkconfig.py
new file mode 100644
index 00000000..210028f5
--- /dev/null
+++ b/cloudbaseinit/tests/plugins/windows/test_networkconfig.py
@@ -0,0 +1,79 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 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 mock
+import re
+import unittest
+
+from cloudbaseinit.openstack.common import cfg
+from cloudbaseinit.plugins.windows import networkconfig
+from cloudbaseinit.tests.metadata import fake_json_response
+
+CONF = cfg.CONF
+
+
+class NetworkConfigPluginPluginTests(unittest.TestCase):
+
+ def setUp(self):
+ self._network_plugin = networkconfig.NetworkConfigPlugin()
+ self.fake_data = fake_json_response.get_fake_metadata_json(
+ '2013-04-04')
+
+ @mock.patch('cloudbaseinit.osutils.factory.OSUtilsFactory.get_os_utils')
+ def _test_execute(self, mock_get_os_utils, search_result, no_adapters):
+ CONF.set_override('network_adapter', 'fake adapter')
+ mock_service = mock.MagicMock()
+ mock_osutils = mock.MagicMock()
+ re.search = mock.MagicMock(return_value=search_result)
+ fake_shared_data = 'fake shared data'
+ mock_service.get_meta_data.return_value = self.fake_data
+ mock_service.get_content.return_value = search_result
+ mock_get_os_utils.return_value = mock_osutils
+ mock_osutils.set_static_network_config.return_value = False
+ if search_result is None:
+ self.assertRaises(Exception, self._network_plugin.execute,
+ mock_service, fake_shared_data)
+ elif no_adapters:
+ CONF.set_override('network_adapter', None)
+ mock_osutils.get_network_adapters.return_value = None
+ self.assertRaises(Exception, self._network_plugin.execute,
+ mock_service, fake_shared_data)
+
+ else:
+ response = self._network_plugin.execute(mock_service,
+ fake_shared_data)
+
+ mock_service.get_meta_data.assert_called_once_with('openstack')
+ mock_service.get_content.assert_called_once_with(
+ 'openstack', self.fake_data['network_config']['content_path'])
+ mock_osutils.set_static_network_config.assert_called_once_with(
+ 'fake adapter', search_result.group('address'),
+ search_result.group('netmask'),
+ search_result.group('broadcast'),
+ search_result.group('gateway'),
+ search_result.group('dnsnameservers').strip().split(' '))
+ self.assertEqual(response, (1, False))
+
+ def test_execute(self):
+ m = mock.MagicMock()
+ self._test_execute(search_result=m, no_adapters=False)
+
+ def test_execute_no_debian(self):
+ self._test_execute(search_result=None, no_adapters=False)
+
+ def test_execute_no_adapters(self):
+ m = mock.MagicMock()
+ self._test_execute(search_result=m, no_adapters=True)
diff --git a/cloudbaseinit/tests/plugins/windows/test_sethostname.py b/cloudbaseinit/tests/plugins/windows/test_sethostname.py
new file mode 100644
index 00000000..275a73d0
--- /dev/null
+++ b/cloudbaseinit/tests/plugins/windows/test_sethostname.py
@@ -0,0 +1,57 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 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 mock
+import unittest
+
+from cloudbaseinit.openstack.common import cfg
+from cloudbaseinit.plugins.windows import sethostname
+from cloudbaseinit.tests.metadata import fake_json_response
+
+CONF = cfg.CONF
+
+
+class SetHostNamePluginPluginTests(unittest.TestCase):
+
+ def setUp(self):
+ self._sethostname_plugin = sethostname.SetHostNamePlugin()
+ self.fake_data = fake_json_response.get_fake_metadata_json(
+ '2013-04-04')
+
+ @mock.patch('cloudbaseinit.osutils.factory.OSUtilsFactory.get_os_utils')
+ def _test_execute(self, mock_get_os_utils, hostname_exists):
+ mock_service = mock.MagicMock()
+ mock_osutils = mock.MagicMock()
+ fake_shared_data = 'fake data'
+ mock_service.get_meta_data.return_value = self.fake_data
+ if hostname_exists is False:
+ del self.fake_data['hostname']
+ mock_get_os_utils.return_value = mock_osutils
+ mock_osutils.set_host_name.return_value = False
+ response = self._sethostname_plugin.execute(mock_service,
+ fake_shared_data)
+ mock_service.get_meta_data.assert_called_once_with('openstack')
+ if hostname_exists is True:
+ mock_get_os_utils.assert_called_once_with()
+ mock_osutils.set_host_name.assert_called_once_with(
+ self.fake_data['hostname'].split('.', 1)[0])
+ self.assertEqual(response, (1, False))
+
+ def test_execute(self):
+ self._test_execute(hostname_exists=True)
+
+ def test_execute_no_hostname(self):
+ self._test_execute(hostname_exists=False)
diff --git a/cloudbaseinit/tests/plugins/windows/test_setuserpassword.py b/cloudbaseinit/tests/plugins/windows/test_setuserpassword.py
new file mode 100644
index 00000000..acf080b4
--- /dev/null
+++ b/cloudbaseinit/tests/plugins/windows/test_setuserpassword.py
@@ -0,0 +1,166 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 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 mock
+import unittest
+
+from cloudbaseinit.openstack.common import cfg
+from cloudbaseinit.plugins import constants
+from cloudbaseinit.plugins.windows import setuserpassword
+from cloudbaseinit.tests.metadata import fake_json_response
+
+CONF = cfg.CONF
+
+
+class SetUserPasswordPluginTests(unittest.TestCase):
+
+ def setUp(self):
+ self._setpassword_plugin = setuserpassword.SetUserPasswordPlugin()
+ self.fake_data = fake_json_response.get_fake_metadata_json(
+ '2013-04-04')
+
+ @mock.patch('base64.b64encode')
+ @mock.patch('cloudbaseinit.utils.crypt.CryptManager'
+ '.load_ssh_rsa_public_key')
+ def test_encrypt_password(self, mock_load_ssh_key, mock_b64encode):
+ mock_rsa = mock.MagicMock()
+ fake_ssh_pub_key = 'fake key'
+ fake_password = 'fake password'
+ mock_load_ssh_key.return_value = mock_rsa
+ mock_rsa.__enter__().public_encrypt.return_value = 'public encrypted'
+ mock_b64encode.return_value = 'encrypted password'
+
+ response = self._setpassword_plugin._encrypt_password(
+ fake_ssh_pub_key, fake_password)
+ print mock_rsa.mock_calls
+
+ mock_load_ssh_key.assert_called_with(fake_ssh_pub_key)
+ mock_rsa.__enter__().public_encrypt.assert_called_with('fake password')
+ mock_b64encode.assert_called_with('public encrypted')
+ self.assertEqual(response, 'encrypted password')
+
+ def _test_get_ssh_public_key(self, data_exists):
+ mock_service = mock.MagicMock()
+ mock_service.get_meta_data.return_value = self.fake_data
+ if data_exists is False:
+ del self.fake_data['public_keys']
+ response = self._setpassword_plugin._get_ssh_public_key(
+ mock_service)
+ self.assertEqual(response, False)
+ else:
+ response = self._setpassword_plugin._get_ssh_public_key(
+ mock_service)
+ mock_service.get_meta_data.assert_called_with(
+ 'openstack', self._setpassword_plugin._post_password_md_ver)
+ self.assertEqual(response, self.fake_data['public_keys']['name'])
+
+ def test_get_ssh_plublic_key(self):
+ self._test_get_ssh_public_key(data_exists=True)
+
+ def test_get_ssh_plublic_key_no_pub_keys(self):
+ self._test_get_ssh_public_key(data_exists=False)
+
+ def test_get_password(self):
+ mock_service = mock.MagicMock()
+ mock_osutils = mock.MagicMock()
+ mock_service.get_meta_data.return_value = self.fake_data
+ CONF.set_override('inject_user_password', False)
+ mock_osutils.generate_random_password.return_value = 'Passw0rd'
+ response = self._setpassword_plugin._get_password(mock_service,
+ mock_osutils)
+ mock_service.get_meta_data.assert_called_with('openstack')
+ mock_osutils.generate_random_password.assert_called_once_with(14)
+ self.assertEqual(response, 'Passw0rd')
+
+ @mock.patch('cloudbaseinit.plugins.windows.setuserpassword.'
+ 'SetUserPasswordPlugin._get_ssh_public_key')
+ @mock.patch('cloudbaseinit.plugins.windows.setuserpassword.'
+ 'SetUserPasswordPlugin._encrypt_password')
+ def _test_set_metadata_password(self, mock_encrypt_password,
+ mock_get_key, ssh_pub_key):
+ fake_passw0rd = 'fake Passw0rd'
+ mock_service = mock.MagicMock()
+ mock_get_key.return_value = ssh_pub_key
+ mock_encrypt_password.return_value = 'encrypted password'
+ mock_service.post_password.return_value = 'value'
+
+ response = self._setpassword_plugin._set_metadata_password(
+ fake_passw0rd, mock_service)
+
+ if ssh_pub_key is None:
+ self.assertEqual(response, True)
+ else:
+ mock_get_key.assert_called_once_with(mock_service)
+ mock_encrypt_password.assert_called_once_with(ssh_pub_key,
+ fake_passw0rd)
+ mock_service.post_password.assert_called_with(
+ 'encrypted password',
+ self._setpassword_plugin._post_password_md_ver)
+ self.assertEqual(response, 'value')
+
+ def test_set_metadata_password_with_ssh_key(self):
+ fake_key = 'fake key'
+ self._test_set_metadata_password(ssh_pub_key=fake_key)
+
+ def test_set_metadata_password_no_ssh_key(self):
+ self._test_set_metadata_password(ssh_pub_key=None)
+
+ @mock.patch('cloudbaseinit.plugins.windows.setuserpassword.'
+ 'SetUserPasswordPlugin._get_password')
+ def test_set_password(self, mock_get_password):
+ mock_service = mock.MagicMock()
+ mock_osutils = mock.MagicMock()
+ mock_get_password.return_value = 'fake password'
+
+ response = self._setpassword_plugin._set_password(mock_service,
+ mock_osutils,
+ 'fake user')
+
+ mock_get_password.assert_called_once_with(mock_service, mock_osutils)
+ mock_osutils.set_user_password.assert_called_once_with('fake user',
+ 'fake password')
+ self.assertEqual(response, 'fake password')
+
+ @mock.patch('cloudbaseinit.plugins.windows.setuserpassword.'
+ 'SetUserPasswordPlugin._set_password')
+ @mock.patch('cloudbaseinit.plugins.windows.setuserpassword.'
+ 'SetUserPasswordPlugin._set_metadata_password')
+ @mock.patch('cloudbaseinit.osutils.factory.OSUtilsFactory.get_os_utils')
+ def test_execute(self, mock_get_os_utils, mock_set_metadata_password,
+ mock_set_password):
+ mock_service = mock.MagicMock()
+ mock_osutils = mock.MagicMock()
+ fake_shared_data = mock.MagicMock()
+ fake_shared_data.get.return_value = 'fake username'
+ mock_service.is_password_set.return_value = False
+ mock_get_os_utils.return_value = mock_osutils
+ mock_osutils.user_exists.return_value = True
+ mock_set_password.return_value = 'fake password'
+
+ response = self._setpassword_plugin.execute(mock_service,
+ fake_shared_data)
+
+ fake_shared_data.get.assert_called_with(
+ constants.SHARED_DATA_USERNAME, CONF.username)
+ mock_service.is_password_set.assert_called_once_with(
+ self._setpassword_plugin._post_password_md_ver)
+ mock_get_os_utils.assert_called_once_with()
+ mock_osutils.user_exists.assert_called_once_with('fake username')
+ mock_set_password.assert_called_once_with(mock_service, mock_osutils,
+ 'fake username')
+ mock_set_metadata_password.assert_called_once_with('fake password',
+ mock_service)
+ self.assertEqual(response, (2, False))
diff --git a/cloudbaseinit/tests/plugins/windows/test_sshpublickeys.py b/cloudbaseinit/tests/plugins/windows/test_sshpublickeys.py
new file mode 100644
index 00000000..88451e94
--- /dev/null
+++ b/cloudbaseinit/tests/plugins/windows/test_sshpublickeys.py
@@ -0,0 +1,70 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 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 mock
+import os
+import unittest
+
+from cloudbaseinit.plugins.windows import sshpublickeys
+from cloudbaseinit.openstack.common import cfg
+from cloudbaseinit.tests.metadata import fake_json_response
+
+CONF = cfg.CONF
+
+
+class SetUserSSHPublicKeysPluginTests(unittest.TestCase):
+
+ def setUp(self):
+ self._set_ssh_keys_plugin = sshpublickeys.SetUserSSHPublicKeysPlugin()
+ self.fake_data = fake_json_response.get_fake_metadata_json(
+ '2013-04-04')
+
+ @mock.patch('cloudbaseinit.osutils.factory.OSUtilsFactory.get_os_utils')
+ @mock.patch('os.path')
+ @mock.patch('os.makedirs')
+ def _test_execute(self, mock_os_makedirs, mock_os_path,
+ mock_get_os_utils, user_home):
+ mock_service = mock.MagicMock()
+ mock_osutils = mock.MagicMock()
+ fake_shared_data = 'fake data'
+ mock_service.get_meta_data.return_value = self.fake_data
+ CONF.set_override('username', 'fake user')
+ mock_get_os_utils.return_value = mock_osutils
+ mock_osutils.get_user_home.return_value = user_home
+ mock_os_path.exists.return_value = False
+
+ if user_home is None:
+ self.assertRaises(Exception, self._set_ssh_keys_plugin,
+ mock_service, fake_shared_data)
+ else:
+ with mock.patch('cloudbaseinit.plugins.windows.sshpublickeys'
+ '.open',
+ mock.mock_open(), create=True):
+ response = self._set_ssh_keys_plugin.execute(mock_service,
+ fake_shared_data)
+ mock_service.get_meta_data.assert_called_with('openstack')
+ mock_osutils.get_user_home.assert_called_with('fake user')
+ self.assertEqual(mock_os_path.join.call_count, 2)
+ mock_os_makedirs.assert_called_once_with(mock_os_path.join())
+
+ self.assertEqual(response, (1, False))
+
+ def test_execute_with_user_home(self):
+ fake_user_home = os.path.join('fake', 'home')
+ self._test_execute(user_home=fake_user_home)
+
+ def test_execute_with_no_user_home(self):
+ self._test_execute(user_home=None)
diff --git a/cloudbaseinit/tests/plugins/windows/test_userdata.py b/cloudbaseinit/tests/plugins/windows/test_userdata.py
new file mode 100644
index 00000000..12d48278
--- /dev/null
+++ b/cloudbaseinit/tests/plugins/windows/test_userdata.py
@@ -0,0 +1,263 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 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 mock
+import tempfile
+import uuid
+import unittest
+
+from cloudbaseinit.metadata.services import base as base_metadata
+from cloudbaseinit.openstack.common import cfg
+from cloudbaseinit.plugins.windows import userdata
+from cloudbaseinit.tests.metadata import fake_json_response
+
+CONF = cfg.CONF
+
+
+class UserDataPluginTest(unittest.TestCase):
+
+ def setUp(self):
+ self._userdata = userdata.UserDataPlugin()
+ self.fake_data = fake_json_response.get_fake_metadata_json(
+ '2013-04-04')
+
+ @mock.patch('os.path')
+ def test_get_plugin_path(self, mock_ospath):
+ mock_ospath.join.return_value = 'fake path'
+ response = self._userdata._get_plugin_path()
+ mock_ospath.join.assert_called_with(
+ mock_ospath.dirname(mock_ospath.dirname(mock_ospath.realpath())),
+ "windows/userdata-plugins")
+ self.assertEqual(response, 'fake path')
+
+ @mock.patch('cloudbaseinit.plugins.windows.userdata.UserDataPlugin'
+ '._process_userdata')
+ def _test_execute(self, mock_process_userdata, user_data, exception):
+ mock_service = mock.MagicMock()
+ fake_shared_data = 'fake data'
+ if exception:
+ e = base_metadata.NotExistingMetadataException()
+ mock_service.side_effect = e
+ else:
+ mock_service.get_user_data.return_value = user_data
+ response = self._userdata.execute(mock_service, fake_shared_data)
+ mock_service.get_user_data.assert_called_with('openstack')
+ if user_data:
+ mock_process_userdata.assert_called_with(user_data)
+ self.assertEqual(response, (1, False))
+
+ def test_execute(self):
+ self._test_execute(user_data=self.fake_data, exception=False)
+
+ def test_execute_no_data(self):
+ self._test_execute(user_data=None, exception=False)
+
+ def test_execute_exception(self):
+ self._test_execute(user_data=None, exception=True)
+
+ @mock.patch('cloudbaseinit.plugins.windows.userdata.handle')
+ @mock.patch('cloudbaseinit.plugins.windows.userdata.UserDataPlugin'
+ '._parse_mime')
+ @mock.patch('cloudbaseinit.plugins.windows.userdata.UserDataPlugin'
+ '._process_part')
+ def _test_process_userdata(self, mock_process_part, mock_parse_mime,
+ mock_handle, user_data):
+ mock_process_part().__iter__.side_effect = ['fake']
+ self._userdata._process_userdata(user_data)
+ print mock_parse_mime.mock_calls
+ print mock_process_part.mock_calls
+ if user_data.startswith('Content-Type: multipart'):
+ mock_parse_mime.assert_called_once_with(user_data)
+ self.assertEqual(mock_process_part.call_count, 1)
+ else:
+ mock_handle.assert_called_once_with(user_data)
+
+ def test_process_userdata_multipart(self):
+ user_data = 'Content-Type: multipart fake data'
+ self._test_process_userdata(user_data=user_data)
+
+ def test_process_userdata(self):
+ user_data = 'fake data'
+ self._test_process_userdata(user_data=user_data)
+
+ @mock.patch('cloudbaseinit.plugins.windows.userdata.UserDataPlugin'
+ '._get_part_handler')
+ @mock.patch('cloudbaseinit.plugins.windows.userdata.UserDataPlugin'
+ '._begin_part_process_event')
+ @mock.patch('cloudbaseinit.plugins.windows.userdata.UserDataPlugin'
+ '._end_part_process_event')
+ def test_process_part(self, mock_end_part_process_event,
+ mock_begin_part_process_event,
+ mock_get_part_handler):
+ mock_part = mock.MagicMock()
+ mock_part_handler = mock.MagicMock()
+ mock_get_part_handler.return_value = mock_part_handler
+
+ self._userdata._process_part(mock_part)
+
+ mock_get_part_handler.assert_called_once_with(mock_part)
+ mock_begin_part_process_event.assert_called_once_with(mock_part)
+ mock_part.get_content_type.assert_called_once_with()
+ mock_part.get_filename.assert_called_once_with()
+ mock_part_handler.process.assert_called_with(mock_part)
+ mock_end_part_process_event.assert_called_with(mock_part)
+
+ @mock.patch('cloudbaseinit.plugins.windows.userdata.UserDataPlugin'
+ '._get_custom_handler')
+ def test_begin_part_process_event(self, mock_get_custom_handler):
+ mock_part = mock.MagicMock()
+ mock_handler = mock.MagicMock()
+ mock_get_custom_handler.return_value = mock_handler
+ self._userdata._begin_part_process_event(mock_part)
+ mock_part.get_filename.assert_called_with()
+ mock_part.get_payload.assert_called_with()
+ mock_handler.assert_called_with("", "__begin__",
+ mock_part.get_filename(),
+ mock_part.get_payload())
+
+ @mock.patch('cloudbaseinit.plugins.windows.userdata.UserDataPlugin'
+ '._get_custom_handler')
+ def test_end_part_process_event(self, mock_get_custom_handler):
+ mock_part = mock.MagicMock()
+ mock_handler = mock.MagicMock()
+ mock_get_custom_handler.return_value = mock_handler
+ self._userdata._end_part_process_event(mock_part)
+ mock_part.get_payload.assert_called_with()
+ mock_handler.assert_called_with("", "__end__",
+ mock_part.get_filename(),
+ mock_part.get_payload())
+
+ def test_get_custom_handler(self):
+ mock_part = mock.MagicMock()
+ mock_part.get_content_type.return_value = 0
+ self._userdata.plugin_set.has_custom_handlers = True
+ self._userdata.plugin_set.custom_handlers = [0]
+ response = self._userdata._get_custom_handler(mock_part)
+ mock_part.get_content_type.assert_called_with()
+ self.assertEqual(response, 0)
+
+ def test_get_part_handler(self):
+ mock_part = mock.MagicMock()
+ mock_part.get_content_type.return_value = 0
+ self._userdata.plugin_set.set = {0: 'fake value'}
+ response = self._userdata._get_part_handler(mock_part)
+ mock_part.get_content_type.assert_called_with()
+ self.assertEqual(response, 'fake value')
+
+ @mock.patch('email.message_from_string')
+ def test_parse_mime(self, mock_message_from_string):
+ mock_msg = mock.MagicMock()
+ mock_message_from_string.return_value = mock_msg
+ response = self._userdata._parse_mime(self.fake_data)
+ mock_message_from_string.assert_called_once_with(self.fake_data)
+ mock_msg.walk.assert_called_once_with()
+ self.assertEqual(response, mock_msg.walk())
+
+ @mock.patch('re.search')
+ @mock.patch('tempfile.gettempdir')
+ @mock.patch('os.remove')
+ @mock.patch('os.path.isdir')
+ @mock.patch('os.path.join')
+ @mock.patch('os.path.exists')
+ @mock.patch('os.path.expandvars')
+ @mock.patch('cloudbaseinit.osutils.factory.OSUtilsFactory.get_os_utils')
+ def _test_handle(self, mock_get_os_utils, mock_path_expandvars,
+ mock_path_exists, mock_path_join, mock_path_isdir,
+ mock_os_remove, mock_gettempdir, mock_re_search,
+ fake_user_data, directory_exists):
+ #TODO: recheck, these are old!
+ mock_osutils = mock.MagicMock()
+ uuid.uuid4 = mock.MagicMock(return_value='randomID')
+ mock_path_join.return_value = 'fake_temp\\randomID'
+ match_instance = mock.MagicMock()
+ path = 'fake_temp\\randomID'
+ args = None
+ mock_get_os_utils.return_value = mock_osutils
+
+ if fake_user_data == '^rem cmd\s':
+ side_effect = [match_instance]
+ number_of_calls = 1
+ extension = '.cmd'
+ args = [path+extension]
+ shell = True
+ elif fake_user_data == '#!':
+ side_effect = [None, match_instance]
+ number_of_calls = 2
+ extension = '.sh'
+ args = ['bash.exe', path+extension]
+ shell = False
+ elif fake_user_data == '#ps1\s':
+ side_effect = [None, None, match_instance]
+ number_of_calls = 3
+ extension = '.ps1'
+ args = ['powershell.exe', '-ExecutionPolicy', 'RemoteSigned',
+ '-NonInteractive', path+extension]
+ shell = False
+ else:
+ side_effect = [None, None, None, match_instance]
+ number_of_calls = 4
+ extension = '.ps1'
+ shell = False
+ if directory_exists:
+ args = [mock_path_expandvars('%windir%\\sysnative\\'
+ 'WindowsPowerShell\\v1.0\\'
+ 'powershell.exe'),
+ '-ExecutionPolicy',
+ 'RemoteSigned', '-NonInteractive', path+extension]
+ mock_path_isdir.return_value = True
+ else:
+ mock_path_isdir.return_value = False
+
+ mock_re_search.side_effect = side_effect
+
+ with mock.patch('cloudbaseinit.plugins.windows.userdata.open',
+ mock.mock_open(), create=True):
+ response = userdata.handle(fake_user_data)
+
+ tempfile.gettempdir.assert_called_once_with()
+
+ mock_path_join.assert_called_once_with(mock_gettempdir(),
+ str(uuid.uuid4()))
+ assert mock_re_search.call_count == number_of_calls
+ if args:
+ mock_osutils.execute_process.assert_called_with(args, shell)
+
+ self.assertEqual(response, (1, False))
+
+ def test_handle_batch(self):
+ fake_user_data = '^rem cmd\s'
+ self._test_handle(fake_user_data=fake_user_data,
+ directory_exists=True)
+
+ def test_handle_shell(self):
+ fake_user_data = '^#!'
+ self._test_handle(fake_user_data=fake_user_data,
+ directory_exists=True)
+
+ def test_handle_powershell(self):
+ fake_user_data = '^#ps1\s'
+ self._test_handle(fake_user_data=fake_user_data,
+ directory_exists=True)
+
+ def test_handle_powershell_sysnative(self):
+ fake_user_data = '#ps1_sysnative\s'
+ self._test_handle(fake_user_data=fake_user_data,
+ directory_exists=True)
+
+ def test_handle_powershell_sysnative_no_sysnative(self):
+ fake_user_data = '#ps1_sysnative\s'
+ self._test_handle(fake_user_data=fake_user_data,
+ directory_exists=False)
diff --git a/cloudbaseinit/tests/plugins/windows/test_userdata_plugins.py b/cloudbaseinit/tests/plugins/windows/test_userdata_plugins.py
new file mode 100644
index 00000000..fe0b9cba
--- /dev/null
+++ b/cloudbaseinit/tests/plugins/windows/test_userdata_plugins.py
@@ -0,0 +1,43 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 Mirantis Inc.
+# 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.
+
+import mock
+import unittest
+
+from cloudbaseinit.plugins.windows import userdata_plugins
+
+
+class MultipartUserDataPluginTest(unittest.TestCase):
+
+ def setUp(self):
+ fake_path = 'fake path'
+ self._userdata = userdata_plugins.PluginSet(fake_path)
+
+ @mock.patch('glob.glob')
+ @mock.patch('cloudbaseinit.plugins.windows.userdata_plugins.'
+ 'load_from_file')
+ def test_load(self, mock_load_from_file, mock_glob):
+ fake_files = ['fake_file.py']
+ mock_plugin = mock.MagicMock()
+ mock_glob.return_value = fake_files
+ mock_load_from_file.return_value = mock_plugin
+
+ self._userdata.load()
+ mock_glob.assert_called_once_with(self._userdata.path + '/*.py')
+ mock_load_from_file.assert_called_once_with('fake_file.py',
+ self._userdata)
+ self.assertEqual(self._userdata.set[mock_plugin.type], mock_plugin)
diff --git a/cloudbaseinit/tests/plugins/windows/test_winrmcertificateauth.py b/cloudbaseinit/tests/plugins/windows/test_winrmcertificateauth.py
new file mode 100644
index 00000000..72889d27
--- /dev/null
+++ b/cloudbaseinit/tests/plugins/windows/test_winrmcertificateauth.py
@@ -0,0 +1,156 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 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 importlib
+import mock
+import sys
+import unittest
+
+_ctypes_mock = mock.MagicMock()
+_win32com_mock = mock.MagicMock()
+_pywintypes_mock = mock.MagicMock()
+
+mock_dict = {'ctypes': _ctypes_mock,
+ 'win32com': _win32com_mock,
+ 'pywintypes': _pywintypes_mock}
+
+
+from cloudbaseinit.openstack.common import cfg
+from cloudbaseinit.plugins import constants
+
+CONF = cfg.CONF
+
+
+class ConfigWinRMCertificateAuthPluginTests(unittest.TestCase):
+ @mock.patch.dict(sys.modules, mock_dict)
+ def setUp(self):
+ winrmcert = importlib.import_module('cloudbaseinit.plugins.windows.'
+ 'winrmcertificateauth')
+ self.x509 = importlib.import_module('cloudbaseinit.plugins.windows'
+ '.x509')
+ self._certif_auth = winrmcert.ConfigWinRMCertificateAuthPlugin()
+
+ def tearDown(self):
+ reload(sys)
+
+ def _test_get_client_auth_cert(self, chunk):
+ mock_service = mock.MagicMock()
+ mock_meta_data = mock.MagicMock()
+ mock_meta = mock.MagicMock()
+ mock_service.get_meta_data.return_value = mock_meta_data
+ mock_meta_data.get.return_value = mock_meta
+ mock_meta.get.side_effect = chunk
+ mock_service.get_user_data.return_value = self.x509.PEM_HEADER
+
+ response = self._certif_auth._get_client_auth_cert(mock_service)
+ mock_service.get_meta_data.assert_called_once_with('openstack')
+ mock_meta_data.get.assert_called_once_with('meta')
+ if chunk == [None]:
+ mock_service.get_user_data.assert_called_once_with('openstack')
+ mock_meta.get.assert_called_once_with('admin_cert0')
+ self.assertEqual(response, self.x509.PEM_HEADER)
+ else:
+ expected = [mock.call('admin_cert0'), mock.call('admin_cert1')]
+ self.assertEqual(mock_meta.get.call_args_list, expected)
+ self.assertEqual(response, 'fake data')
+
+ def test_get_client_auth_cert(self):
+ chunk = ['fake data', None]
+ self._test_get_client_auth_cert(chunk=chunk)
+
+ def test_get_client_auth_cert_no_cert_data(self):
+ self._test_get_client_auth_cert(chunk=[None])
+
+ def _test_get_credentials(self, fake_user, fake_password):
+ mock_shared_data = mock.MagicMock()
+ mock_shared_data.get.side_effect = [fake_user, fake_password]
+ if fake_user is None or fake_password is None:
+ self.assertRaises(Exception,
+ self._certif_auth._get_credentials,
+ mock_shared_data)
+ else:
+ response = self._certif_auth._get_credentials(mock_shared_data)
+ expected = [mock.call(constants.SHARED_DATA_USERNAME),
+ mock.call(constants.SHARED_DATA_PASSWORD)]
+ self.assertEqual(mock_shared_data.get.call_args_list, expected)
+ mock_shared_data.__setitem__.assert_called_once_with(
+ 'admin_password', None)
+ self.assertEqual(response, (fake_user, fake_password))
+
+ def test_test_get_credentials(self):
+ self._test_get_credentials(fake_user='fake user',
+ fake_password='fake password')
+
+ def test_test_get_credentials_no_user(self):
+ self._test_get_credentials(fake_user=None,
+ fake_password='fake password')
+
+ def test_test_get_credentials_no_password(self):
+ self._test_get_credentials(fake_user='fake user',
+ fake_password=None)
+
+ @mock.patch('cloudbaseinit.plugins.windows.winrmcertificateauth'
+ '.ConfigWinRMCertificateAuthPlugin._get_credentials')
+ @mock.patch('cloudbaseinit.plugins.windows.winrmcertificateauth'
+ '.ConfigWinRMCertificateAuthPlugin._get_client_auth_cert')
+ @mock.patch('cloudbaseinit.plugins.windows.x509.CryptoAPICertManager'
+ '.import_cert')
+ @mock.patch('cloudbaseinit.plugins.windows.winrmconfig.WinRMConfig')
+ def _test_execute(self, mock_WinRMConfig, mock_import_cert,
+ mock_get_client_auth_cert, mock_get_credentials,
+ cert_data, cert_upn):
+ mock_service = mock.MagicMock()
+ mock_cert_thumprint = mock.MagicMock()
+ fake_credentials = ('fake user', 'fake password')
+ mock_shared_data = mock.MagicMock()
+ mock_get_client_auth_cert.return_value = cert_data
+ mock_get_credentials.return_value = fake_credentials
+ mock_import_cert.return_value = (mock_cert_thumprint, cert_upn)
+ mock_WinRMConfig.get_cert_mapping.return_value = True
+
+ response = self._certif_auth.execute(mock_service,
+ mock_shared_data)
+ if not cert_data or not cert_upn:
+ self.assertEqual(response, (1, False))
+ else:
+ mock_get_client_auth_cert.assert_called_once_with(mock_service)
+ mock_get_credentials.assert_called_once_with(mock_shared_data)
+ mock_import_cert.assert_called_once_with(
+ cert_data, store_name=self.x509.STORE_NAME_ROOT)
+
+ mock_WinRMConfig().set_auth_config.assert_called_once_with(
+ certificate=True)
+ mock_WinRMConfig().get_cert_mapping.assert_called_once_with(
+ mock_cert_thumprint, cert_upn)
+ mock_WinRMConfig().delete_cert_mapping.assert_called_once_with(
+ mock_cert_thumprint, cert_upn)
+ mock_WinRMConfig().create_cert_mapping.assert_called_once_with(
+ mock_cert_thumprint, cert_upn, 'fake user',
+ 'fake password')
+ self.assertEqual(response, (1, False))
+
+ def test_execute(self):
+ cert_data = 'fake cert data'
+ cert_upn = mock.MagicMock()
+ self._test_execute(cert_data=cert_data, cert_upn=cert_upn)
+
+ def test_execute_no_cert_data(self):
+ cert_upn = mock.MagicMock()
+ self._test_execute(cert_data=None, cert_upn=cert_upn)
+
+ def test_execute_no_cert_upn(self):
+ cert_data = 'fake cert data'
+ self._test_execute(cert_data=cert_data, cert_upn=None)
diff --git a/cloudbaseinit/tests/plugins/windows/test_winrmconfig.py b/cloudbaseinit/tests/plugins/windows/test_winrmconfig.py
new file mode 100644
index 00000000..b6945659
--- /dev/null
+++ b/cloudbaseinit/tests/plugins/windows/test_winrmconfig.py
@@ -0,0 +1,378 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 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 mock
+import sys
+import unittest
+
+from cloudbaseinit.openstack.common import cfg
+if sys.platform == 'win32':
+ from cloudbaseinit.plugins.windows import winrmconfig
+
+CONF = cfg.CONF
+
+
+@unittest.skipUnless(sys.platform == "win32", "requires Windows")
+class WinRMConfigTests(unittest.TestCase):
+
+ def setUp(self):
+ self._winrmconfig = winrmconfig.WinRMConfig()
+
+ @mock.patch('win32com.client.Dispatch')
+ def test_get_wsman_session(self, mock_Dispatch):
+ mock_wsman = mock.MagicMock()
+ mock_Dispatch.return_value = mock_wsman
+ response = self._winrmconfig._get_wsman_session()
+ mock_Dispatch.assert_called_once_with('WSMan.Automation')
+ mock_wsman.CreateSession.assert_called_once_with()
+ self.assertEqual(response, mock_wsman.CreateSession())
+
+ @mock.patch('re.match')
+ def test_get_node_tag(self, mock_match):
+ mock_tag = mock.MagicMock()
+ response = self._winrmconfig._get_node_tag(mock_tag)
+ mock_match.assert_called_once_with("^{.*}(.*)$", mock_tag)
+ self.assertEqual(response, mock_match().groups().__getitem__())
+
+ @mock.patch('xml.etree.ElementTree.fromstring')
+ @mock.patch('cloudbaseinit.plugins.windows.winrmconfig.WinRMConfig.'
+ '_get_node_tag')
+ def _test_parse_listener_xml(self, mock_get_node_tag, mock_fromstring,
+ data_xml, tag=None, text='Fake'):
+ mock_node = mock.MagicMock()
+ mock_node.tag = tag
+ mock_node.text = text
+ fake_tree = [mock_node]
+ mock_get_node_tag.return_value = tag
+ mock_fromstring.return_value = fake_tree
+ response = self._winrmconfig._parse_listener_xml(data_xml=data_xml)
+ if data_xml is None:
+ self.assertEqual(response, None)
+ else:
+ mock_fromstring.assert_called_once_with(data_xml)
+ mock_get_node_tag.assert_called_once_with(tag)
+ if tag is "ListeningOn":
+ self.assertEqual(response, {'ListeningOn': ['Fake']})
+ elif tag is "Enabled":
+ if text is 'true':
+ self.assertEqual(response, {'ListeningOn': [],
+ 'Enabled': True})
+ else:
+ self.assertEqual(response, {'ListeningOn': [],
+ 'Enabled': False})
+ elif tag is 'Port':
+ self.assertEqual(response, {'ListeningOn': [],
+ 'Port': int(text)})
+ else:
+ self.assertEqual(response, {'ListeningOn': [],
+ tag: text})
+
+ def test_parse_listener_xml_no_data(self):
+ self._test_parse_listener_xml(data_xml=None)
+
+ def test_parse_listener_xml_listening_on(self):
+ self._test_parse_listener_xml(data_xml='fake data', tag="ListeningOn")
+
+ def test_parse_listener_xml_enabled_true(self):
+ self._test_parse_listener_xml(data_xml='fake data',
+ tag="Enabled", text='true')
+
+ def test_parse_listener_xml_enabled_false(self):
+ self._test_parse_listener_xml(data_xml='fake data', tag='Enabled',
+ text='false')
+
+ def test_parse_listener_xml_port(self):
+ self._test_parse_listener_xml(data_xml='fake data', tag='Port',
+ text='9999')
+
+ def test_parse_listener_xml_other_tag(self):
+ self._test_parse_listener_xml(data_xml='fake data', tag='fake tag',
+ text='fake text')
+
+ @mock.patch('xml.etree.ElementTree.fromstring')
+ @mock.patch('cloudbaseinit.plugins.windows.winrmconfig.WinRMConfig.'
+ '_get_node_tag')
+ def _test_parse_cert_mapping_xml(self, mock_get_node_tag,
+ mock_fromstring, data_xml, tag=None,
+ text='Fake'):
+ mock_node = mock.MagicMock()
+ mock_node.tag = tag
+ mock_node.text = text
+ fake_tree = [mock_node]
+ mock_get_node_tag.return_value = tag
+ mock_fromstring.return_value = fake_tree
+ response = self._winrmconfig._parse_cert_mapping_xml(data_xml=data_xml)
+ if data_xml is None:
+ self.assertEqual(response, None)
+ else:
+ mock_fromstring.assert_called_once_with(data_xml)
+ mock_get_node_tag.assert_called_once_with(tag)
+ if tag is "Enabled":
+ if text is 'true':
+ self.assertEqual(response, {'Enabled': True})
+ else:
+ self.assertEqual(response, {'Enabled': False})
+ else:
+ self.assertEqual(response, {tag: text})
+
+ def test_parse_cert_mapping_xml_no_data(self):
+ self._test_parse_cert_mapping_xml(data_xml=None)
+
+ def test_parse_cert_mapping_xml_enabled_true(self):
+ self._test_parse_listener_xml(data_xml='fake data',
+ tag="Enabled", text='true')
+
+ def test_parse_cert_mapping_xml_enabled_false(self):
+ self._test_parse_listener_xml(data_xml='fake data', tag='Enabled',
+ text='false')
+
+ def test_parse_cert_mapping_xml_other_tag(self):
+ self._test_parse_listener_xml(data_xml='fake data', tag='fake tag',
+ text='fake text')
+
+ def _test_get_xml_bool(self, value):
+ response = self._winrmconfig._get_xml_bool(value)
+ if value:
+ self.assertEqual(response, 'true')
+ else:
+ self.assertEqual(response, 'false')
+
+ def test_get_xml_bool_true(self):
+ self._test_get_xml_bool(value='fake value')
+
+ def test_get_xml_bool_false(self):
+ self._test_get_xml_bool(value=None)
+
+ @mock.patch('cloudbaseinit.plugins.windows.winrmconfig.WinRMConfig.'
+ '_get_wsman_session')
+ def _test_get_resource(self, mock_get_wsman_session, resource):
+ fake_session = mock.MagicMock()
+ fake_uri = 'fake:\\uri'
+ fake_session.Get.side_effect = [resource]
+ mock_get_wsman_session.return_value = fake_session
+ if resource is Exception:
+ self.assertRaises(Exception, self._winrmconfig._get_resource,
+ fake_uri)
+ else:
+ response = self._winrmconfig._get_resource(fake_uri)
+ mock_get_wsman_session.assert_called_once_with()
+ fake_session.Get.assert_called_once_with(fake_uri)
+ self.assertEqual(response, resource)
+
+ def test_get_resource(self):
+ self._test_get_resource(resource='fake resource')
+
+ def test_get_resource_exception(self):
+ self._test_get_resource(resource=Exception)
+
+ @mock.patch('cloudbaseinit.plugins.windows.winrmconfig.WinRMConfig.'
+ '_get_wsman_session')
+ def test_delete_resource(self, mock_get_wsman_session):
+ fake_session = mock.MagicMock()
+ fake_uri = 'fake:\\uri'
+ mock_get_wsman_session.return_value = fake_session
+ self._winrmconfig._delete_resource(fake_uri)
+ fake_session.Delete.assert_called_once_with(fake_uri)
+
+ @mock.patch('cloudbaseinit.plugins.windows.winrmconfig.WinRMConfig.'
+ '_get_wsman_session')
+ def test_create_resource(self, mock_get_wsman_session):
+ fake_session = mock.MagicMock()
+ fake_uri = 'fake:\\uri'
+ mock_get_wsman_session.return_value = fake_session
+ self._winrmconfig._create_resource(fake_uri, 'fake data')
+ fake_session.Create.assert_called_once_with(fake_uri, 'fake data')
+
+ @mock.patch('cloudbaseinit.plugins.windows.winrmconfig.WinRMConfig.'
+ '_parse_cert_mapping_xml')
+ @mock.patch('cloudbaseinit.plugins.windows.winrmconfig.WinRMConfig.'
+ '_get_resource')
+ def test_get_cert_mapping(self, mock_get_resource,
+ mock_parse_cert_mapping_xml):
+ fake_dict = {'issuer': 'issuer',
+ 'subject': 'subject',
+ 'uri': 'fake:\\uri'}
+ mock_parse_cert_mapping_xml.return_value = 'fake response'
+ mock_get_resource.return_value = 'fake resource'
+ response = self._winrmconfig.get_cert_mapping('issuer', 'subject',
+ uri='fake:\\uri')
+ mock_parse_cert_mapping_xml.assert_called_with('fake resource')
+ mock_get_resource.assert_called_with(
+ self._winrmconfig._SERVICE_CERTMAPPING_URI % fake_dict)
+ self.assertEqual(response, 'fake response')
+
+ @mock.patch('cloudbaseinit.plugins.windows.winrmconfig.WinRMConfig.'
+ '_delete_resource')
+ def test_delete_cert_mapping(self, mock_delete_resource):
+ fake_dict = {'issuer': 'issuer',
+ 'subject': 'subject',
+ 'uri': 'fake:\\uri'}
+ self._winrmconfig.delete_cert_mapping('issuer', 'subject',
+ uri='fake:\\uri')
+ mock_delete_resource.assert_called_with(
+ self._winrmconfig._SERVICE_CERTMAPPING_URI % fake_dict)
+
+ @mock.patch('cloudbaseinit.plugins.windows.winrmconfig.WinRMConfig.'
+ '_get_xml_bool')
+ @mock.patch('cloudbaseinit.plugins.windows.winrmconfig.WinRMConfig.'
+ '_create_resource')
+ def test_create_cert_mapping(self, mock_create_resource,
+ mock_get_xml_bool):
+ fake_dict = {'issuer': 'issuer',
+ 'subject': 'subject',
+ 'uri': 'fake:\\uri'}
+ mock_get_xml_bool.return_value = True
+ self._winrmconfig.create_cert_mapping(
+ issuer='issuer', subject='subject', username='fake user',
+ password='fake password', uri='fake:\\uri', enabled=True)
+ mock_get_xml_bool.assert_called_once_with(True)
+ mock_create_resource.assert_called_once_with(
+ self._winrmconfig._SERVICE_CERTMAPPING_URI % fake_dict,
+ ''
+ '%(enabled)s'
+ '%(password)s'
+ '%(username)s'
+ '' % {'enabled': True,
+ 'username': 'fake user',
+ 'password': 'fake password'})
+
+ @mock.patch('cloudbaseinit.plugins.windows.winrmconfig.WinRMConfig.'
+ '_get_resource')
+ @mock.patch('cloudbaseinit.plugins.windows.winrmconfig.WinRMConfig.'
+ '_parse_listener_xml')
+ def test_get_listener(self, mock_parse_listener_xml, mock_get_resource):
+ dict = {'protocol': 'HTTPS',
+ 'address': 'fake:\\address'}
+ mock_get_resource.return_value = 'fake resource'
+ mock_parse_listener_xml.return_value = 'fake response'
+ response = self._winrmconfig.get_listener(protocol='HTTPS',
+ address="fake:\\address")
+ mock_get_resource.assert_called_with(
+ self._winrmconfig._SERVICE_LISTENER_URI % dict)
+ mock_parse_listener_xml.assert_called_once_with('fake resource')
+ self.assertEqual(response, 'fake response')
+
+ @mock.patch('cloudbaseinit.plugins.windows.winrmconfig.WinRMConfig.'
+ '_delete_resource')
+ def test_delete_listener(self, mock_delete_resource):
+ dict = {'protocol': 'HTTPS',
+ 'address': 'fake:\\address'}
+ self._winrmconfig.delete_listener(protocol='HTTPS',
+ address="fake:\\address")
+ mock_delete_resource.assert_called_with(
+ self._winrmconfig._SERVICE_LISTENER_URI % dict)
+
+ @mock.patch('cloudbaseinit.plugins.windows.winrmconfig.WinRMConfig.'
+ '_create_resource')
+ @mock.patch('cloudbaseinit.plugins.windows.winrmconfig.WinRMConfig.'
+ '_get_xml_bool')
+ def test_create_listener(self, mock_get_xml_bool, mock_create_resource):
+ dict = {'protocol': 'HTTPS',
+ 'address': 'fake:\\address'}
+ mock_get_xml_bool.return_value = True
+ self._winrmconfig.create_listener(protocol='HTTPS',
+ cert_thumbprint=None,
+ address="fake:\\address",
+ enabled=True)
+ mock_create_resource.assert_called_once_with(
+ self._winrmconfig._SERVICE_LISTENER_URI % dict,
+ ''
+ '%(enabled)s'
+ '%(cert_thumbprint)s'
+ ''
+ 'wsman'
+ '' % {"enabled": True,
+ "cert_thumbprint": None})
+
+ @mock.patch('xml.etree.ElementTree.fromstring')
+ @mock.patch('cloudbaseinit.plugins.windows.winrmconfig.WinRMConfig.'
+ '_get_node_tag')
+ @mock.patch('cloudbaseinit.plugins.windows.winrmconfig.WinRMConfig.'
+ '_get_resource')
+ def test_get_auth_config(self, mock_get_resource, mock_get_node_tag,
+ mock_fromstring):
+ mock_node = mock.MagicMock()
+ mock_node.tag = 'tag'
+ mock_node.text = 'value'
+ fake_tree = [mock_node]
+ mock_get_resource.return_value = 'fake data xml'
+ mock_fromstring.return_value = fake_tree
+ mock_get_node_tag.return_value = 'tag'
+
+ response = self._winrmconfig.get_auth_config()
+
+ mock_get_resource.assert_called_with(
+ self._winrmconfig._SERVICE_AUTH_URI)
+ mock_fromstring.assert_called_once_with('fake data xml')
+ mock_get_node_tag.assert_called_once_with(mock_node.tag)
+ self.assertEqual(response, {'tag': 'value'})
+
+ @mock.patch('xml.etree.ElementTree.fromstring')
+ @mock.patch('xml.etree.ElementTree.tostring')
+ @mock.patch('cloudbaseinit.plugins.windows.winrmconfig.WinRMConfig.'
+ '_get_wsman_session')
+ @mock.patch('cloudbaseinit.plugins.windows.winrmconfig.WinRMConfig.'
+ '_get_xml_bool')
+ def test_set_auth_config(self, mock_get_xml_bool, mock_get_wsman_session,
+ mock_tostring, mock_fromstring):
+ mock_session = mock.MagicMock()
+ mock_tree = mock.MagicMock()
+ mock_node = mock.MagicMock()
+ base_url = 'http://schemas.microsoft.com/wbem/wsman/1/config/service/'
+ expected_find = [
+ mock.call('.//cfg:Certificate', namespaces={
+ 'cfg': base_url + 'auth'}),
+ mock.call('.//cfg:Kerberos',
+ namespaces={'cfg': base_url + 'auth'}),
+ mock.call('.//cfg:CbtHardeningLevel',
+ namespaces={'cfg': base_url + 'auth'}),
+ mock.call('.//cfg:Negotiate',
+ namespaces={'cfg': base_url + 'auth'}),
+ mock.call('.//cfg:CredSSP',
+ namespaces={'cfg': base_url + 'auth'}),
+ mock.call('.//cfg:Basic',
+ namespaces={'cfg': base_url + 'auth'})]
+ expected_get_xml_bool = [mock.call('certificate'),
+ mock.call('kerberos'),
+ mock.call('cbt_hardening_level'),
+ mock.call('negotiate'),
+ mock.call('credSSP'),
+ mock.call('basic')]
+
+ mock_get_wsman_session.return_value = mock_session
+ mock_session.Get.return_value = 'fake xml'
+ mock_fromstring.return_value = mock_tree
+ mock_get_xml_bool.return_value = 'true'
+ mock_tostring.return_value = 'fake xml'
+ mock_tree.find.return_value = mock_node
+ mock_node.text.lower.return_value = 'old value'
+
+ self._winrmconfig.set_auth_config(
+ basic='basic', kerberos='kerberos', negotiate='negotiate',
+ certificate='certificate', credSSP='credSSP',
+ cbt_hardening_level='cbt_hardening_level')
+ self.assertEqual(mock_tree.find.call_args_list, expected_find)
+ self.assertEqual(mock_get_xml_bool.call_args_list,
+ expected_get_xml_bool)
+
+ mock_get_wsman_session.assert_called_once_with()
+ mock_session.Get.assert_called_with(
+ self._winrmconfig._SERVICE_AUTH_URI)
+ mock_fromstring.assert_called_once_with('fake xml')
+ mock_session.Put.assert_called_with(
+ self._winrmconfig._SERVICE_AUTH_URI, 'fake xml')
diff --git a/cloudbaseinit/tests/plugins/windows/test_winrmlistener.py b/cloudbaseinit/tests/plugins/windows/test_winrmlistener.py
new file mode 100644
index 00000000..a2252139
--- /dev/null
+++ b/cloudbaseinit/tests/plugins/windows/test_winrmlistener.py
@@ -0,0 +1,117 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 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 importlib
+import mock
+import sys
+import unittest
+
+from cloudbaseinit.openstack.common import cfg
+
+CONF = cfg.CONF
+_mock_wintypes = mock.MagicMock()
+mock_dict = {'ctypes.wintypes': _mock_wintypes}
+
+
+class ConfigWinRMListenerPluginTests(unittest.TestCase):
+ @mock.patch.dict(sys.modules, mock_dict)
+ def setUp(self):
+ winrmlistener = importlib.import_module('cloudbaseinit.plugins.'
+ 'windows.winrmlistener')
+ self._winrmlistener = winrmlistener.ConfigWinRMListenerPlugin()
+
+ def _test_check_winrm_service(self, service_exists):
+ mock_osutils = mock.MagicMock()
+ mock_osutils.check_service_exists.return_value = service_exists
+ mock_osutils.SERVICE_START_MODE_MANUAL = 'fake start'
+ mock_osutils.SERVICE_START_MODE_DISABLED = 'fake start'
+ mock_osutils.SERVICE_STATUS_STOPPED = 'fake status'
+ mock_osutils.get_service_start_mode.return_value = 'fake start'
+ mock_osutils.get_service_status.return_value = 'fake status'
+
+ response = self._winrmlistener._check_winrm_service(mock_osutils)
+ if not service_exists:
+ self.assertEqual(response, False)
+ else:
+
+ mock_osutils.get_service_start_mode.assert_called_once_with(
+ self._winrmlistener._winrm_service_name)
+ mock_osutils.get_service_start_mode.assert_called_once_with(
+ self._winrmlistener._winrm_service_name)
+ mock_osutils.set_service_start_mode.assert_called_once_with(
+ self._winrmlistener._winrm_service_name,
+ mock_osutils .SERVICE_START_MODE_AUTOMATIC)
+ mock_osutils.get_service_status.assert_called_once_with(
+ self._winrmlistener._winrm_service_name)
+ mock_osutils.start_service.assert_called_once_with(
+ self._winrmlistener._winrm_service_name)
+ self.assertEqual(response, True)
+
+ def test_check_winrm_service(self):
+ self._test_check_winrm_service(service_exists=True)
+
+ def test_check_winrm_service_no_service(self):
+ self._test_check_winrm_service(service_exists=False)
+
+ @mock.patch('cloudbaseinit.osutils.factory.OSUtilsFactory.get_os_utils')
+ @mock.patch('cloudbaseinit.plugins.windows.winrmlistener.'
+ 'ConfigWinRMListenerPlugin._check_winrm_service')
+ @mock.patch('cloudbaseinit.plugins.windows.winrmconfig.WinRMConfig')
+ @mock.patch('cloudbaseinit.plugins.windows.x509.CryptoAPICertManager'
+ '.create_self_signed_cert')
+ def _test_execute(self, mock_create_cert, mock_WinRMConfig,
+ mock_check_winrm_service, mock_get_os_utils,
+ service_status):
+ mock_service = mock.MagicMock()
+ mock_listener_config = mock.MagicMock()
+ mock_cert_thumbprint = mock.MagicMock()
+ shared_data = 'fake data'
+ mock_osutils = mock.MagicMock()
+ mock_get_os_utils.return_value = mock_osutils
+ mock_check_winrm_service.return_value = service_status
+ mock_create_cert.return_value = mock_cert_thumbprint
+ mock_WinRMConfig().get_listener.return_value = mock_listener_config
+ mock_listener_config.get.return_value = 9999
+
+ response = self._winrmlistener.execute(mock_service, shared_data)
+
+ mock_get_os_utils.assert_called_once_with()
+ mock_check_winrm_service.assert_called_once_with(mock_osutils)
+
+ if not service_status:
+ self.assertEqual(response, (2, False))
+ else:
+ mock_WinRMConfig().set_auth_config.assert_called_once_with(
+ basic=CONF.winrm_enable_basic_auth)
+ mock_create_cert.assert_called_once_with(
+ self._winrmlistener._cert_subject)
+
+ mock_WinRMConfig().get_listener.assert_called_with(
+ protocol="HTTPS")
+ mock_WinRMConfig().delete_listener.assert_called_once_with(
+ protocol="HTTPS")
+ mock_WinRMConfig().create_listener.assert_called_once_with(
+ protocol="HTTPS", cert_thumbprint=mock_cert_thumbprint)
+ mock_listener_config.get.assert_called_once_with("Port")
+ mock_osutils.firewall_create_rule.assert_called_once_with(
+ "WinRM HTTPS", 9999, mock_osutils.PROTOCOL_TCP)
+ self.assertEqual(response, (1, False))
+
+ def test_execute(self):
+ self._test_execute(service_status=True)
+
+ def test_execute_service_status_is_false(self):
+ self._test_execute(service_status=False)
diff --git a/cloudbaseinit/tests/plugins/windows/test_x509.py b/cloudbaseinit/tests/plugins/windows/test_x509.py
new file mode 100644
index 00000000..de942160
--- /dev/null
+++ b/cloudbaseinit/tests/plugins/windows/test_x509.py
@@ -0,0 +1,383 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 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 mock
+import sys
+import unittest
+
+from cloudbaseinit.openstack.common import cfg
+if sys.platform == 'win32':
+ from cloudbaseinit.plugins.windows import cryptoapi
+ from cloudbaseinit.plugins.windows import x509
+
+CONF = cfg.CONF
+
+
+@unittest.skipUnless(sys.platform == "win32", "requires Windows")
+class CryptoAPICertManagerTests(unittest.TestCase):
+
+ def setUp(self):
+ self._x509 = x509.CryptoAPICertManager()
+
+ @mock.patch('cloudbaseinit.plugins.windows.x509.free')
+ @mock.patch('ctypes.c_ubyte')
+ @mock.patch('ctypes.POINTER')
+ @mock.patch('ctypes.cast')
+ @mock.patch('cloudbaseinit.plugins.windows.x509.malloc')
+ @mock.patch('ctypes.byref')
+ @mock.patch('ctypes.wintypes.DWORD')
+ @mock.patch('cloudbaseinit.plugins.windows.cryptoapi.'
+ 'CertGetCertificateContextProperty')
+ def _test_get_cert_thumprint(self,
+ mock_CertGetCertificateContextProperty,
+ mock_DWORD, mock_byref, mock_malloc,
+ mock_cast, mock_POINTER, mock_c_ubyte,
+ mock_free, ret_val):
+
+ mock_pointer = mock.MagicMock()
+ fake_cert_context_p = 'fake context'
+ mock_DWORD().value = 10
+ mock_CertGetCertificateContextProperty.return_value = ret_val
+ mock_POINTER.return_value = mock_pointer
+ mock_cast().contents = [16]
+ if not ret_val:
+ self.assertRaises(cryptoapi.CryptoAPIException,
+ self._x509._get_cert_thumprint,
+ fake_cert_context_p)
+ else:
+ expected = [mock.call(fake_cert_context_p,
+ cryptoapi.CERT_SHA1_HASH_PROP_ID,
+ None, mock_byref()),
+ mock.call(fake_cert_context_p,
+ cryptoapi.CERT_SHA1_HASH_PROP_ID,
+ mock_malloc(), mock_byref())]
+ response = self._x509._get_cert_thumprint(fake_cert_context_p)
+ self.assertEqual(
+ mock_CertGetCertificateContextProperty.call_args_list,
+ expected)
+ mock_malloc.assert_called_with(mock_DWORD())
+ mock_cast.assert_called_with(mock_malloc(), mock_pointer)
+ mock_free.assert_called_with(mock_malloc())
+ self.assertEqual(response, '10')
+
+ def test_get_cert_thumprint(self):
+ self._test_get_cert_thumprint(ret_val=True)
+
+ def test_get_cert_thumprint_GetCertificateContextProperty_exception(self):
+ self._test_get_cert_thumprint(ret_val=False)
+
+ @mock.patch('cloudbaseinit.plugins.windows.cryptoapi.'
+ 'CryptDestroyKey')
+ @mock.patch('cloudbaseinit.plugins.windows.cryptoapi.'
+ 'CryptReleaseContext')
+ @mock.patch('cloudbaseinit.plugins.windows.cryptoapi.'
+ 'CryptGenKey')
+ @mock.patch('cloudbaseinit.plugins.windows.cryptoapi.'
+ 'CryptAcquireContext')
+ @mock.patch('ctypes.byref')
+ @mock.patch('ctypes.wintypes.HANDLE')
+ def _test_generate_key(self, mock_HANDLE, mock_byref,
+ mock_CryptAcquireContext, mock_CryptGenKey,
+ mock_CryptReleaseContext, mock_CryptDestroyKey,
+ acquired_context, generate_key_ret_val):
+ mock_CryptAcquireContext.return_value = acquired_context
+ mock_CryptGenKey.return_value = generate_key_ret_val
+ if not acquired_context:
+ self.assertRaises(cryptoapi.CryptoAPIException,
+ self._x509._generate_key,
+ 'fake container', True)
+ else:
+ if generate_key_ret_val is None:
+ self.assertRaises(cryptoapi.CryptoAPIException,
+ self._x509._generate_key, 'fake container',
+ True)
+ mock_byref.assert_called_with(mock_HANDLE())
+ else:
+ self._x509._generate_key('fake container', True)
+ mock_CryptAcquireContext.assert_called_with(
+ mock_byref(), 'fake container', None,
+ cryptoapi.PROV_RSA_FULL, cryptoapi.CRYPT_MACHINE_KEYSET)
+ mock_CryptGenKey.assert_called_with(mock_HANDLE(),
+ cryptoapi.AT_SIGNATURE,
+ 0x08000000, mock_HANDLE())
+ mock_CryptDestroyKey.assert_called_once_with(
+ mock_HANDLE())
+ mock_CryptReleaseContext.assert_called_once_with(
+ mock_HANDLE(), 0)
+
+ def test_generate_key(self):
+ self._test_generate_key(acquired_context=True,
+ generate_key_ret_val='fake key')
+
+ def test_generate_key_GetCertificateContextProperty_exception(self):
+ self._test_generate_key(acquired_context=False,
+ generate_key_ret_val='fake key')
+
+ def test_generate_key_CryptGenKey_exception(self):
+ self._test_generate_key(acquired_context=True,
+ generate_key_ret_val=None)
+
+ @mock.patch('cloudbaseinit.plugins.windows.x509.free')
+ @mock.patch('copy.copy')
+ @mock.patch('ctypes.byref')
+ @mock.patch('cloudbaseinit.plugins.windows.x509.malloc')
+ @mock.patch('ctypes.POINTER')
+ @mock.patch('ctypes.cast')
+ @mock.patch('cloudbaseinit.plugins.windows.x509.CryptoAPICertManager'
+ '._generate_key')
+ @mock.patch('cloudbaseinit.plugins.windows.x509.CryptoAPICertManager'
+ '._get_cert_thumprint')
+ @mock.patch('uuid.uuid4')
+ @mock.patch('ctypes.wintypes.DWORD')
+ @mock.patch('cloudbaseinit.plugins.windows.cryptoapi.'
+ 'CertStrToName')
+ @mock.patch('cloudbaseinit.plugins.windows.cryptoapi.'
+ 'CRYPTOAPI_BLOB')
+ @mock.patch('cloudbaseinit.plugins.windows.cryptoapi.'
+ 'CRYPT_KEY_PROV_INFO')
+ @mock.patch('cloudbaseinit.plugins.windows.cryptoapi.'
+ 'CRYPT_ALGORITHM_IDENTIFIER')
+ @mock.patch('cloudbaseinit.plugins.windows.cryptoapi.'
+ 'SYSTEMTIME')
+ @mock.patch('cloudbaseinit.plugins.windows.cryptoapi.'
+ 'GetSystemTime')
+ @mock.patch('cloudbaseinit.plugins.windows.cryptoapi.'
+ 'CertCreateSelfSignCertificate')
+ @mock.patch('cloudbaseinit.plugins.windows.cryptoapi.'
+ 'CertAddEnhancedKeyUsageIdentifier')
+ @mock.patch('cloudbaseinit.plugins.windows.cryptoapi.'
+ 'CertOpenStore')
+ @mock.patch('cloudbaseinit.plugins.windows.cryptoapi.'
+ 'CertAddCertificateContextToStore')
+ @mock.patch('cloudbaseinit.plugins.windows.cryptoapi.'
+ 'CertCloseStore')
+ @mock.patch('cloudbaseinit.plugins.windows.cryptoapi.'
+ 'CertFreeCertificateContext')
+ def _test_create_self_signed_cert(self, mock_CertFreeCertificateContext,
+ mock_CertCloseStore,
+ mock_CertAddCertificateContextToStore,
+ mock_CertOpenStore,
+ mock_CertAddEnhancedKeyUsageIdentifier,
+ mock_CertCreateSelfSignCertificate,
+ mock_GetSystemTime, mock_SYSTEMTIME,
+ mock_CRYPT_ALGORITHM_IDENTIFIER,
+ mock_CRYPT_KEY_PROV_INFO,
+ mock_CRYPTOAPI_BLOB,
+ mock_CertStrToName, mock_DWORD,
+ mock_uuid4, mock_get_cert_thumprint,
+ mock_generate_key, mock_cast,
+ mock_POINTER, mock_malloc, mock_byref,
+ mock_copy, mock_free, certstr,
+ certificate, enhanced_key,
+ store_handle, context_to_store):
+
+ mock_uuid4.return_value = 'fake_name'
+ mock_CertCreateSelfSignCertificate.return_value = certificate
+ mock_CertAddEnhancedKeyUsageIdentifier.return_value = enhanced_key
+ mock_CertStrToName.return_value = certstr
+ mock_CertOpenStore.return_value = store_handle
+ mock_CertAddCertificateContextToStore.return_value = context_to_store
+ if (certstr is None or certificate is None or enhanced_key is None
+ or store_handle is None or context_to_store is None):
+ self.assertRaises(cryptoapi.CryptoAPIException,
+ self._x509.create_self_signed_cert,
+ 'fake subject', 10, True, x509.STORE_NAME_MY)
+ else:
+ response = self._x509.create_self_signed_cert(
+ subject='fake subject')
+ mock_cast.assert_called_with(mock_malloc(), mock_POINTER())
+ mock_CRYPTOAPI_BLOB.assert_called_once_with()
+ mock_CRYPT_KEY_PROV_INFO.assert_called_once_with()
+ mock_CRYPT_ALGORITHM_IDENTIFIER.assert_called_once_with()
+ mock_SYSTEMTIME.assert_called_once_with()
+ mock_GetSystemTime.assert_called_once_with(mock_byref())
+ mock_copy.assert_called_once_with(mock_SYSTEMTIME())
+ mock_CertCreateSelfSignCertificate.assert_called_once_with(
+ None, mock_byref(), 0, mock_byref(),
+ mock_byref(), mock_byref(), mock_byref(), None)
+ mock_CertAddEnhancedKeyUsageIdentifier.assert_called_with(
+ mock_CertCreateSelfSignCertificate(),
+ cryptoapi.szOID_PKIX_KP_SERVER_AUTH)
+ mock_CertOpenStore.assert_called_with(
+ cryptoapi.CERT_STORE_PROV_SYSTEM, 0, 0,
+ cryptoapi.CERT_SYSTEM_STORE_LOCAL_MACHINE,
+ unicode(x509.STORE_NAME_MY))
+ mock_get_cert_thumprint.assert_called_once_with(
+ mock_CertCreateSelfSignCertificate())
+
+ mock_CertCloseStore.assert_called_once_with(store_handle, 0)
+ mock_CertFreeCertificateContext.assert_called_once_with(
+ mock_CertCreateSelfSignCertificate())
+ mock_free.assert_called_once_with(mock_cast())
+
+ self.assertEqual(response, mock_get_cert_thumprint())
+
+ mock_generate_key.assert_called_once_with('fake_name', True)
+
+ def test_create_self_signed_cert(self):
+ self._test_create_self_signed_cert(certstr='fake cert name',
+ certificate='fake certificate',
+ enhanced_key='fake key',
+ store_handle='fake handle',
+ context_to_store='fake context')
+
+ def test_create_self_signed_cert_CertStrToName_fail(self):
+ self._test_create_self_signed_cert(certstr=None,
+ certificate='fake certificate',
+ enhanced_key='fake key',
+ store_handle='fake handle',
+ context_to_store='fake context')
+
+ def test_create_self_signed_cert_CertCreateSelfSignCertificate_fail(self):
+ self._test_create_self_signed_cert(certstr='fake cert name',
+ certificate=None,
+ enhanced_key='fake key',
+ store_handle='fake handle',
+ context_to_store='fake context')
+
+ def test_create_self_signed_cert_AddEnhancedKeyUsageIdentifier_fail(self):
+ self._test_create_self_signed_cert(certstr='fake cert name',
+ certificate='fake certificate',
+ enhanced_key=None,
+ store_handle='fake handle',
+ context_to_store='fake context')
+
+ def test_create_self_signed_cert_CertOpenStore_fail(self):
+ self._test_create_self_signed_cert(certstr='fake cert name',
+ certificate='fake certificate',
+ enhanced_key='fake key',
+ store_handle=None,
+ context_to_store='fake context')
+
+ def test_create_self_signed_cert_AddCertificateContextToStore_fail(self):
+ self._test_create_self_signed_cert(certstr='fake cert name',
+ certificate='fake certificate',
+ enhanced_key='fake key',
+ store_handle='fake handle',
+ context_to_store=None)
+
+ def test_get_cert_base64(self):
+ fake_cert_data = ''
+ fake_cert_data += x509.PEM_HEADER + '\n'
+ fake_cert_data += 'fake cert' + '\n'
+ fake_cert_data += x509.PEM_FOOTER
+ response = self._x509._get_cert_base64(fake_cert_data)
+ self.assertEqual(response, 'fake cert')
+
+ @mock.patch('cloudbaseinit.plugins.windows.x509.free')
+ @mock.patch('cloudbaseinit.plugins.windows.x509.CryptoAPICertManager'
+ '._get_cert_thumprint')
+ @mock.patch('cloudbaseinit.plugins.windows.cryptoapi.'
+ 'CertCloseStore')
+ @mock.patch('cloudbaseinit.plugins.windows.cryptoapi.'
+ 'CertFreeCertificateContext')
+ @mock.patch('cloudbaseinit.plugins.windows.cryptoapi.'
+ 'CertGetNameString')
+ @mock.patch('cloudbaseinit.plugins.windows.cryptoapi.'
+ 'CertAddEncodedCertificateToStore')
+ @mock.patch('cloudbaseinit.plugins.windows.cryptoapi.'
+ 'CertOpenStore')
+ @mock.patch('cloudbaseinit.plugins.windows.cryptoapi.'
+ 'CryptStringToBinaryA')
+ @mock.patch('cloudbaseinit.plugins.windows.x509.CryptoAPICertManager'
+ '._get_cert_base64')
+ @mock.patch('ctypes.POINTER')
+ @mock.patch('cloudbaseinit.plugins.windows.x509.malloc')
+ @mock.patch('ctypes.cast')
+ @mock.patch('ctypes.byref')
+ @mock.patch('ctypes.wintypes.DWORD')
+ @mock.patch('ctypes.create_unicode_buffer')
+ def _test_import_cert(self, mock_create_unicode_buffer, mock_DWORD,
+ mock_byref, mock_cast,
+ mock_malloc, mock_POINTER, mock_get_cert_base64,
+ mock_CryptStringToBinaryA, mock_CertOpenStore,
+ mock_CertAddEncodedCertificateToStore,
+ mock_CertGetNameString,
+ mock_CertFreeCertificateContext,
+ mock_CertCloseStore, mock_get_cert_thumprint,
+ mock_free, crypttstr, store_handle, add_enc_cert,
+ upn_len):
+ fake_cert_data = ''
+ fake_cert_data += x509.PEM_HEADER + '\n'
+ fake_cert_data += 'fake cert' + '\n'
+ fake_cert_data += x509.PEM_FOOTER
+ mock_get_cert_base64.return_value = 'fake cert'
+ mock_CryptStringToBinaryA.return_value = crypttstr
+ mock_CertOpenStore.return_value = store_handle
+ mock_CertAddEncodedCertificateToStore.return_value = add_enc_cert
+ mock_CertGetNameString.side_effect = [2, upn_len]
+
+ expected = [mock.call('fake cert', len('fake cert'),
+ cryptoapi.CRYPT_STRING_BASE64, None,
+ mock_byref(), None, None),
+ mock.call('fake cert', len('fake cert'),
+ cryptoapi.CRYPT_STRING_BASE64, mock_cast(),
+ mock_byref(), None, None)]
+ expected2 = [mock.call(mock_POINTER()(), cryptoapi.CERT_NAME_UPN_TYPE,
+ 0, None, None, 0),
+ mock.call(mock_POINTER()(), cryptoapi.CERT_NAME_UPN_TYPE,
+ 0, None, mock_create_unicode_buffer(), 2)]
+
+ if (not crypttstr or store_handle is None or add_enc_cert is None or
+ upn_len != 2):
+ self.assertRaises(cryptoapi.CryptoAPIException,
+ self._x509.import_cert, fake_cert_data, True,
+ x509.STORE_NAME_MY)
+ else:
+ response = self._x509.import_cert(fake_cert_data)
+ mock_cast.assert_called_with(mock_malloc(), mock_POINTER())
+ self.assertEqual(mock_CryptStringToBinaryA.call_args_list,
+ expected)
+ mock_CertOpenStore.assert_called_with(
+ cryptoapi.CERT_STORE_PROV_SYSTEM, 0, 0,
+ cryptoapi.CERT_SYSTEM_STORE_LOCAL_MACHINE,
+ unicode(x509.STORE_NAME_MY))
+ mock_CertAddEncodedCertificateToStore.assert_called_with(
+ mock_CertOpenStore(),
+ cryptoapi.X509_ASN_ENCODING | cryptoapi.PKCS_7_ASN_ENCODING,
+ mock_cast(), mock_DWORD(),
+ cryptoapi.CERT_STORE_ADD_REPLACE_EXISTING, mock_byref())
+ mock_create_unicode_buffer.assert_called_with(2)
+ self.assertEqual(mock_CertGetNameString.call_args_list, expected2)
+ mock_get_cert_thumprint.assert_called_once_with(mock_POINTER()())
+ mock_CertFreeCertificateContext.assert_called_once_with(
+ mock_POINTER()())
+ mock_CertCloseStore.assert_called_once_with(
+ mock_CertOpenStore(), 0)
+ mock_free.assert_called_once_with(mock_cast())
+ self.assertEqual(response, (mock_get_cert_thumprint(),
+ mock_create_unicode_buffer().value))
+ mock_get_cert_base64.assert_called_with(fake_cert_data)
+
+ def test_import_cert(self):
+ self._test_import_cert(crypttstr=True, store_handle='fake handle',
+ add_enc_cert='fake encoded cert', upn_len=2)
+
+ def test_import_cert_CryptStringToBinaryA_fail(self):
+ self._test_import_cert(crypttstr=False, store_handle='fake handle',
+ add_enc_cert='fake encoded cert', upn_len=2)
+
+ def test_import_cert_CertOpenStore_fail(self):
+ self._test_import_cert(crypttstr=False, store_handle=None,
+ add_enc_cert='fake encoded cert', upn_len=2)
+
+ def test_import_cert_CertAddEncodedCertificateToStore_fail(self):
+ self._test_import_cert(crypttstr=True, store_handle='fake handle',
+ add_enc_cert=None, upn_len=2)
+
+ def test_import_cert_CertGetNameString_fail(self):
+ self._test_import_cert(crypttstr=True, store_handle='fake handle',
+ add_enc_cert='fake encoded cert', upn_len=3)
diff --git a/cloudbaseinit/tests/plugins/windows/userdata_plugins/__init__.py b/cloudbaseinit/tests/plugins/windows/userdata_plugins/__init__.py
new file mode 100644
index 00000000..7227b295
--- /dev/null
+++ b/cloudbaseinit/tests/plugins/windows/userdata_plugins/__init__.py
@@ -0,0 +1,15 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 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.
diff --git a/cloudbaseinit/tests/plugins/windows/userdata_plugins/test_heathandler.py b/cloudbaseinit/tests/plugins/windows/userdata_plugins/test_heathandler.py
new file mode 100644
index 00000000..cba1467f
--- /dev/null
+++ b/cloudbaseinit/tests/plugins/windows/userdata_plugins/test_heathandler.py
@@ -0,0 +1,42 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 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 importlib
+import mock
+import unittest
+
+from cloudbaseinit.openstack.common import cfg
+from cloudbaseinit.plugins.windows import userdata_plugins
+#the name of the module includes "-", importlib.import_module is needed:
+heathandler = importlib.import_module("cloudbaseinit.plugins.windows"
+ ".userdata-plugins.heathandler")
+
+CONF = cfg.CONF
+
+
+class HeatUserDataHandlerTests(unittest.TestCase):
+
+ def setUp(self):
+ parent_set = userdata_plugins.PluginSet
+ self._heathandler = heathandler.HeatUserDataHandler(parent_set)
+
+ @mock.patch('cloudbaseinit.plugins.windows.userdata.handle')
+ def test_process(self, mock_handle):
+ mock_part = mock.MagicMock()
+ mock_part.get_filename.return_value = "cfn-userdata"
+ self._heathandler.process(mock_part)
+ mock_part.get_filename.assert_called_once_with()
+ mock_handle.assert_called_once_with(mock_part.get_payload())
diff --git a/cloudbaseinit/tests/plugins/windows/userdata_plugins/test_parthandler.py b/cloudbaseinit/tests/plugins/windows/userdata_plugins/test_parthandler.py
new file mode 100644
index 00000000..c1b0b3da
--- /dev/null
+++ b/cloudbaseinit/tests/plugins/windows/userdata_plugins/test_parthandler.py
@@ -0,0 +1,87 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 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 importlib
+import mock
+import os
+import unittest
+
+from cloudbaseinit.openstack.common import cfg
+from cloudbaseinit.plugins.windows import userdata_plugins
+#the name of the module includes "-", importlib.import_module is needed:
+parthandler = importlib.import_module("cloudbaseinit.plugins.windows"
+ ".userdata-plugins.parthandler")
+
+CONF = cfg.CONF
+
+
+class PartHandlerScriptHandlerTests(unittest.TestCase):
+
+ def setUp(self):
+ parent_set = userdata_plugins.PluginSet('fake_path')
+ self._parthandler = parthandler.PartHandlerScriptHandler(parent_set)
+
+ @mock.patch('imp.load_source')
+ @mock.patch('imp.load_compiled')
+ @mock.patch('cloudbaseinit.plugins.windows.userdata-plugins.parthandler'
+ '.__import__', create=True)
+ def _test_load_from_file(self, mock__import__, mock_load_compiled,
+ mock_load_source, filepath):
+ mock_module = mock.MagicMock()
+ mock__import__.return_value = mock_module
+ mod_name, file_ext = os.path.splitext(os.path.split(filepath)[-1])
+ response = parthandler.load_from_file(filepath, 'fake_function')
+ print response
+ if file_ext.lower() == '.py':
+ mock_load_source.assert_called_with('path', filepath)
+ elif file_ext.lower() == '.pyc':
+ mock_load_compiled.assert_called_with('path', filepath)
+ mock__import__.assert_called_once_with('path')
+ self.assertEqual(response, mock_module.fake_function)
+
+ def test_load_from_file_py(self):
+ fake_file_path = os.path.join(os.path.join('fake', 'file'), 'path')
+ self._test_load_from_file(filepath=fake_file_path + '.py')
+
+ def test_load_from_file_pyc(self):
+ fake_file_path = os.path.join(os.path.join('fake', 'file'), 'path')
+ self._test_load_from_file(filepath=fake_file_path + '.pyc')
+
+ @mock.patch('cloudbaseinit.plugins.windows.userdata-plugins.parthandler.'
+ 'load_from_file')
+ def test_process(self, mock_load_from_file):
+ mock_part = mock.MagicMock()
+ mock_part.get_filename.return_value = 'fake_name'
+ handler_path = self._parthandler.parent_set.path + "/part-handler/"
+ handler_path += 'fake_name'
+ expected = [mock.call(),
+ mock.call(handler_path, "list_types"),
+ mock.call(handler_path, "handle_part")]
+ mock_load_from_file().return_value = ['fake part']
+ with mock.patch("cloudbaseinit.plugins.windows.userdata-plugins."
+ "parthandler.open", mock.mock_open(), create=True):
+ self._parthandler.process(mock_part)
+
+ print mock_load_from_file.mock_calls
+ print self._parthandler.parent_set.custom_handlers
+
+ mock_part.get_filename.assert_called_once_with()
+ mock_part.get_payload.assert_called_once_with()
+ self.assertEqual(mock_load_from_file.call_args_list, expected)
+ self.assertEqual(self._parthandler.parent_set.has_custom_handlers,
+ True)
+ self.assertEqual(self._parthandler.parent_set.custom_handlers,
+ {'fake part': mock_load_from_file()})
diff --git a/cloudbaseinit/tests/plugins/windows/userdata_plugins/test_shellscript.py b/cloudbaseinit/tests/plugins/windows/userdata_plugins/test_shellscript.py
new file mode 100644
index 00000000..bc61693b
--- /dev/null
+++ b/cloudbaseinit/tests/plugins/windows/userdata_plugins/test_shellscript.py
@@ -0,0 +1,84 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 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 importlib
+import mock
+import os
+import unittest
+
+from cloudbaseinit.openstack.common import cfg
+from cloudbaseinit.plugins.windows import userdata_plugins
+#the name of the module includes "-", importlib.import_module is needed:
+shellscript = importlib.import_module("cloudbaseinit.plugins.windows"
+ ".userdata-plugins.shellscript")
+
+CONF = cfg.CONF
+
+
+class ShellScriptHandlerTests(unittest.TestCase):
+
+ def setUp(self):
+ parent_set = userdata_plugins.PluginSet('fake_path')
+ self._shellscript = shellscript.ShellScriptHandler(parent_set)
+
+ @mock.patch('cloudbaseinit.osutils.factory.OSUtilsFactory.get_os_utils')
+ @mock.patch('tempfile.gettempdir')
+ def _test_process(self, mock_gettempdir, mock_get_os_utils, filename,
+ exception=False):
+ fake_dir_path = os.path.join("fake", "dir")
+ mock_osutils = mock.MagicMock()
+ mock_part = mock.MagicMock()
+ mock_part.get_filename.return_value = filename
+ mock_gettempdir.return_value = fake_dir_path
+
+ mock_get_os_utils.return_value = mock_osutils
+
+ if exception:
+ mock_osutils.execute_process.side_effect = [Exception]
+
+ with mock.patch("cloudbaseinit.plugins.windows.userdata-plugins."
+ "shellscript.open", mock.mock_open(), create=True):
+ response = self._shellscript.process(mock_part)
+
+ mock_part.get_filename.assert_called_once_with()
+ mock_gettempdir.assert_called_once_with()
+ if filename.endswith(".cmd"):
+ mock_osutils.execute_process.assert_called_with(
+ [os.path.join(fake_dir_path, filename)], True)
+ elif filename.endswith(".sh"):
+ mock_osutils.execute_process.assert_called_with(
+ ['bash.exe', os.path.join(fake_dir_path, filename)], False)
+ elif filename.endswith(".ps1"):
+ mock_osutils.execute_process.assert_called_with(
+ ['powershell.exe', '-ExecutionPolicy', 'RemoteSigned',
+ '-NonInteractive', os.path.join(fake_dir_path, filename)],
+ False)
+ self.assertFalse(response)
+
+ def test_process_cmd(self):
+ self._test_process(filename='fake.cmd')
+
+ def test_process_sh(self):
+ self._test_process(filename='fake.cmd')
+
+ def test_process_ps1(self):
+ self._test_process(filename='fake.cmd')
+
+ def test_process_other(self):
+ self._test_process(filename='fake.other')
+
+ def test_process_exception(self):
+ self._test_process(filename='fake.cmd', exception=True)
diff --git a/cloudbaseinit/tests/test_init.py b/cloudbaseinit/tests/test_init.py
new file mode 100644
index 00000000..21cf0a5f
--- /dev/null
+++ b/cloudbaseinit/tests/test_init.py
@@ -0,0 +1,141 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 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 mock
+import unittest
+import sys
+
+from cloudbaseinit import init
+from cloudbaseinit.plugins import base
+from cloudbaseinit.openstack.common import cfg
+
+CONF = cfg.CONF
+_win32com_mock = mock.MagicMock()
+_comtypes_mock = mock.MagicMock()
+_pywintypes_mock = mock.MagicMock()
+_ctypes_mock = mock.MagicMock()
+_ctypes_util_mock = mock.MagicMock()
+mock_dict = {'ctypes.util': _ctypes_util_mock,
+ 'win32com': _win32com_mock,
+ 'comtypes': _comtypes_mock,
+ 'pywintypes': _pywintypes_mock,
+ 'ctypes': _ctypes_mock}
+
+
+class InitManagerTest(unittest.TestCase):
+ @mock.patch.dict(sys.modules, mock_dict)
+ def setUp(self):
+ self.osutils = mock.MagicMock()
+ self.plugin = mock.MagicMock()
+ self._init = init.InitManager()
+
+ def tearDown(self):
+ reload(sys)
+ reload(init)
+
+ def test_get_plugin_status(self):
+ self.osutils.get_config_value.return_value = 1
+ response = self._init._get_plugin_status(self.osutils, 'fake plugin')
+ self.osutils.get_config_value.assert_called_once_with(
+ 'fake plugin', self._init._PLUGINS_CONFIG_SECTION)
+ self.assertTrue(response == 1)
+
+ def test_set_plugin_status(self):
+
+ self._init._set_plugin_status(self.osutils, 'fake plugin', 'status')
+ self.osutils.set_config_value.assert_called_once_with(
+ 'fake plugin', 'status', self._init._PLUGINS_CONFIG_SECTION)
+
+ @mock.patch('cloudbaseinit.init.InitManager._get_plugin_status')
+ @mock.patch('cloudbaseinit.init.InitManager._set_plugin_status')
+ def _test_exec_plugin(self, status, mock_set_plugin_status,
+ mock_get_plugin_status):
+ fake_name = 'fake name'
+ self.plugin.get_name.return_value = fake_name
+ self.plugin.execute.return_value = (status, True)
+ mock_get_plugin_status.return_value = status
+
+ response = self._init._exec_plugin(osutils=self.osutils,
+ service='fake service',
+ plugin=self.plugin,
+ shared_data='shared data')
+
+ mock_get_plugin_status.assert_called_once_with(self.osutils,
+ fake_name)
+ if status is base.PLUGIN_EXECUTE_ON_NEXT_BOOT:
+ self.plugin.execute.assert_called_once_with('fake service',
+ 'shared data')
+ mock_set_plugin_status.assert_called_once_with(self.osutils,
+ fake_name, status)
+ self.assertTrue(response)
+
+ def test_test_exec_plugin_execution_done(self):
+ self._test_exec_plugin(base.PLUGIN_EXECUTION_DONE)
+
+ def test_test_exec_plugin(self):
+ self._test_exec_plugin(base.PLUGIN_EXECUTE_ON_NEXT_BOOT)
+
+ def _test_check_plugin_os_requirements(self, requirements):
+ sys.platform = 'win32'
+ fake_name = 'fake name'
+ self.plugin.get_name.return_value = fake_name
+ self.plugin.get_os_requirements.return_value = requirements
+
+ response = self._init._check_plugin_os_requirements(self.osutils,
+ self.plugin)
+
+ self.plugin.get_name.assert_called_once_with()
+ self.plugin.get_os_requirements.assert_called_once_with()
+ if requirements[0] == 'win32':
+ self.assertTrue(response)
+ else:
+ self.assertFalse(response)
+
+ def test_check_plugin_os_requirements(self):
+ self._test_check_plugin_os_requirements(('win32', (5, 2)))
+
+ def test_check_plugin_os_requirements_other_requirenments(self):
+ self._test_check_plugin_os_requirements(('linux', (5, 2)))
+
+ @mock.patch('cloudbaseinit.init.InitManager'
+ '._check_plugin_os_requirements')
+ @mock.patch('cloudbaseinit.init.InitManager._exec_plugin')
+ @mock.patch('cloudbaseinit.plugins.factory.PluginFactory.load_plugins')
+ @mock.patch('cloudbaseinit.osutils.factory.OSUtilsFactory.get_os_utils')
+ @mock.patch('cloudbaseinit.metadata.factory.MetadataServiceFactory.'
+ 'get_metadata_service')
+ def test_configure_host(self, mock_get_metadata_service,
+ mock_get_os_utils, mock_load_plugins,
+ mock_exec_plugin,
+ mock_check_os_requirements):
+ fake_service = mock.MagicMock()
+ fake_plugin = mock.MagicMock()
+ mock_load_plugins.return_value = [fake_plugin]
+ mock_get_os_utils.return_value = self.osutils
+ mock_get_metadata_service.return_value = fake_service
+ fake_service.get_name.return_value = 'fake name'
+
+ self._init.configure_host()
+
+ self.osutils.wait_for_boot_completion.assert_called_once()
+ mock_get_metadata_service.assert_called_once_with()
+ fake_service.get_name.assert_called_once_with()
+ mock_check_os_requirements.assert_called_once_with(self.osutils,
+ fake_plugin)
+ mock_exec_plugin.assert_called_once_with(self.osutils, fake_service,
+ fake_plugin, {})
+ fake_service.cleanup.assert_called_once_with()
+ self.osutils.reboot.assert_called_once_with()