3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
17 from collections import namedtuple
21 from crl.remotesession.remotesession import RemoteSession
22 from hostcli import HostCli
23 from .cluster import (
27 from .testutils.profiles import (
33 from .testutils.ippool import IPPool
34 from .testutils.host import (
38 from .metasingleton import MetaSingleton
41 class ClusterMocks(namedtuple('ClusterMocks', ['remotesession',
48 @six.add_metaclass(abc.ABCMeta)
49 class ClusterVerifierBase(object):
51 _mgmt_target = MgmtTarget(host='host',
55 _sudoshelldicts = [{'shellname': 'BashShell', 'cmd': 'sudo bash'}]
57 def __init__(self, cluster_mocks):
58 self._mocks = cluster_mocks
59 self._ippool = IPPool()
60 self._hosts = [h for h in self._hosts_gen()]
62 MetaSingleton.clear(Cluster)
66 if self._cluster is None:
70 def _setup_cluster(self):
71 self._cluster = self._create_cluster()
73 def verify_get_host(self):
75 actual_host = self.cluster.get_host(h.name)
76 assert actual_host.shelldicts == self._expected_shelldicts(h), (
77 'expected: {}, got: {}'.format(
78 self._expected_shelldicts(h),
79 actual_host.shelldicts))
80 assert actual_host.network_domain == h.expected_network_domain, (
81 'expected: {expected}, got: {actual}'.format(
82 expected=h.expected_network_domain,
83 actual=actual_host.network_domain))
84 assert actual_host.is_dpdk == h.expected_is_dpdk()
86 def verify_master_external_ip(self):
87 for master in self._masters:
88 actual_ip = self.cluster.get_host(master.name).external_ip
89 assert actual_ip == master.external_ip, (
90 'Expected external_ip : {expected}, actual: {actual}'.format(
91 expected=master.external_ip,
97 if MasterProfile in h.service_profiles:
100 def verify_get_hosts_with_profiles(self):
101 for profs, eh in self._expected_restricted_profs.items():
102 expected_hosts = sorted(eh)
103 hosts = self.cluster.get_hosts_with_profiles(*profs)
104 assert hosts == expected_hosts, (
105 'Expected hosts: {e}, got: {g}'.format(e=expected_hosts, g=hosts))
107 def verify_hosts_containing(self):
108 for p in self._profs_combinations():
109 hosts = set(self.cluster.get_hosts_containing(*p))
110 expected_hosts = set(self._get_expected_hosts_containing(set(p)))
111 assert set(hosts) == expected_hosts, (
112 'Expected hosts: {e}, got: {g}'.format(e=expected_hosts, g=hosts))
114 def verify_initialize_remotesession(self):
115 mock_remotesession = mock.create_autospec(RemoteSession).return_value
116 self.cluster.initialize_remotesession(mock_remotesession)
117 self._verify_initialize_mock_calls(mock_remotesession)
119 def _verify_initialize_mock_calls(self, mock_remotesession):
120 mock_remotesession.set_envcreator.assert_called_once_with(
121 self._mocks.envcreator.return_value)
122 self._verify_normal_and_sudo_mgmt(mock_remotesession)
123 self._verify_remotescript_default(mock_remotesession)
124 for h in self.cluster.get_hosts():
125 self._verify_normal_and_sudo_host(h, mock_remotesession)
127 def _verify_normal_and_sudo_mgmt(self, mock_remotesession):
128 mgmtshelldicts = self.cluster.get_mgmt_shelldicts()
129 self._should_be_in_set_runner(mock.call(mgmtshelldicts),
130 mock_remotesession=mock_remotesession)
131 sudodicts = mgmtshelldicts + self._sudoshelldicts
132 self._should_be_in_set_runner(mock.call(shelldicts=sudodicts,
133 name='sudo-default'),
134 mock_remotesession=mock_remotesession)
136 def _verify_remotescript_default(self, mock_remotesession):
137 self._should_be_in_set_target(mock.call(host=self._mgmt_target.host,
138 username=self._mgmt_target.user,
139 password=self._mgmt_target.password,
140 name='remotescript-default'),
141 mock_remotesession=mock_remotesession)
143 def _should_be_in_set_target(self, call, mock_remotesession):
144 set_target_calls = self._get_set_target_calls(mock_remotesession)
145 assert call in set_target_calls, (
146 '{call} is not in {set_target_calls}'.format(
148 set_target_calls=set_target_calls))
151 def _get_set_target_calls(mock_remotesession):
152 return mock_remotesession.set_target.mock_calls
154 def _verify_normal_and_sudo_host(self, host, mock_remotesession):
155 self._should_be_in_set_runner(mock.call(shelldicts=host.shelldicts,
157 mock_remotesession=mock_remotesession)
158 sudodicts = host.shelldicts + self._sudoshelldicts
159 self._should_be_in_set_runner(mock.call(shelldicts=sudodicts,
160 name='sudo-{}'.format(host.name)),
161 mock_remotesession=mock_remotesession)
163 def verify_create_remotesession(self):
164 self._verify_initialize_mock_calls(self.cluster.create_remotesession())
166 def _should_be_in_set_runner(self, call, mock_remotesession):
167 set_runner_target_calls = self._get_set_runner_target_calls(mock_remotesession)
168 assert call in set_runner_target_calls, (
169 '{call} is not in {set_runner_target_calls}'.format(
171 set_runner_target_calls=set_runner_target_calls))
173 def verify_initialize_hostcli(self):
174 mock_hostcli = mock.create_autospec(HostCli)
175 self.cluster.initialize_hostcli(mock_hostcli)
176 mock_hostcli.initialize.assert_called_once_with(
177 self._mocks.remotesession.return_value)
179 def verify_create_hostcli(self):
180 assert self.cluster.create_hostcli() == self._mocks.hostcli.return_value
181 assert self._mocks.hostcli.return_value.initialize.mock_calls == [
182 mock.call(self._mocks.remotesession.return_value) for _ in range(2)]
184 def verify_create_user_with_roles(self):
185 roles = ['role1', 'role2']
186 create_user = self._mocks.usermanager.return_value.create_user_with_roles
187 assert self.cluster.create_user_with_roles(*roles) == create_user.return_value
188 self._mocks.usermanager.assert_called_once_with(
189 hostcli_factory=self._cluster.create_hostcli)
190 create_user.assert_called_once_with(*roles)
192 def verify_delete_users(self):
193 self.cluster.delete_users()
194 self._mocks.usermanager.assert_called_once_with(
195 hostcli_factory=self._cluster.create_hostcli)
196 self._mocks.usermanager.return_value.delete_users.assert_called_once_with()
198 def verify_envcreator(self):
199 self._setup_cluster()
200 self._mocks.envcreator.assert_called_once_with(
201 remotesession_factory=self.cluster.create_remotesession,
202 usermanager=self._mocks.usermanager.return_value)
205 def _get_set_runner_target_calls(mock_remotesession):
206 return mock_remotesession.set_runner_target.mock_calls
208 def verify_cluster_config_caching(self):
209 self._create_cluster()
211 self._setup_cluster_and_verify(cluster)
213 def verify_mgmt_shelldicts(self):
214 assert self.cluster.get_mgmt_shelldicts() == [self._mgmt_target.asdict()]
216 def verify_is_dpdk(self):
217 assert self.cluster.is_dpdk() == self._expected_is_dpdk
219 def verify_get_hosts_with_dpdk(self):
220 assert self.cluster.get_hosts_with_dpdk() == sorted(self._expected_hosts_with_dpdk())
222 def _expected_hosts_with_dpdk(self):
223 for h in self._hosts:
224 if h.expected_is_dpdk():
228 def _expected_is_dpdk(self):
229 for h in self._hosts:
230 if h.expected_is_dpdk():
236 def _hosts_gen(self):
237 """Return generator of :class:`.testutils.Host` instances."""
239 def _create_cluster(self):
242 self._setup_cluster_and_verify(c)
245 def _setup_cluster_and_verify(self, cluster):
246 self._mocks.hostcli.return_value.run.return_value = self._configprops
247 cluster.set_profiles(master=str(MasterProfile()), worker=str(WorkerProfile()))
248 cluster.initialize(**self._mgmt_target.asdict())
249 self._verify_after_initialize(cluster)
251 def _verify_after_initialize(self, cluster):
252 assert len(self._hosts) == len(cluster.get_hosts()), (
253 len(self._hosts), len(cluster.get_hosts()))
255 hostcli = self._mocks.hostcli.return_value
256 hostcli.initialize.assert_called_once_with(
257 self._mocks.remotesession.return_value)
258 hostcli.run.assert_called_once_with('config-manager list properties')
260 def _get_expected_hosts_containing(self, profs):
262 for p, h in self._expected_profs.items():
263 if profs.issubset(p):
264 hosts = hosts.union(set(h))
268 def _expected_restricted_profs(self):
269 def profile_filter(prof):
270 return prof in [MasterProfile, WorkerProfile, StorageProfile]
272 return self._get_expected_profs_for_filter(profile_filter)
275 def _expected_profs(self):
276 return self._get_expected_profs_for_filter(lambda prof: True)
278 def _get_expected_profs_for_filter(self, profile_filter):
280 for host in self._hosts:
281 key = frozenset([str(s()) for s in host.service_profiles if profile_filter(s)])
284 p[key].append(host.name)
288 def _profs_combinations():
289 profs = [str(p()) for p in [MasterProfile,
293 for r in range(1, len(profs) + 1):
294 for p in itertools.combinations(profs, r):
297 def _expected_shelldicts(self, host):
298 if MasterProfile in host.service_profiles:
299 return [{'host': host.external_ip,
300 'user': self._mgmt_target.user,
301 'password': self._mgmt_target.password}]
302 return [self._mgmt_target.asdict(),
303 {'host': host.internal_ip,
304 'user': self._mgmt_target.user,
305 'password': self._mgmt_target.password}]
308 def _configprops(self):
309 return list(self._hosts_config_gen()) + [self._get_hosts_network_profiles()]
311 def _hosts_config_gen(self):
312 for h in self._hosts:
313 yield {'property': '{host}.networking'.format(host=h.name),
314 'value': h.networking}
316 yield {'property': 'cloud.hosts',
317 'value': {h.name: h.host_dict for h in self._hosts}}
319 def _get_hosts_network_profiles(self):
320 return {'property': 'cloud.network_profiles',
321 'value': self._get_network_profile_details()}
323 def _get_network_profile_details(self):
325 for h in self._hosts:
326 d.update(h.network_profile_details)
330 def _master_gen(self):
331 return MasterGenerator(self._ippool).gen
334 def _worker_gen(self):
335 return WorkerGenerator(self._ippool).gen
338 class Type1Verifier(ClusterVerifierBase):
339 # pylint: disable=not-callable
340 def _hosts_gen(self):
341 return itertools.chain(self._master_gen(3),
345 class Type2Verifier(ClusterVerifierBase):
347 def _hosts_gen(self):
348 return itertools.chain(self._master_gen(3),
349 self._dpdk_worker_gen(2))
352 def _dpdk_worker_gen(self):
353 return DpdkWorkerGenerator(self._ippool).gen
356 class CorruptedVerifier(Type1Verifier):
358 def _hosts_config_gen(self):
359 yield {'property': 'not-relevant',
360 'value': 'not-relevant'}
362 def verify_corrupted_raises(self):
363 with pytest.raises(ClusterError) as exinfo:
364 self.cluster.get_host('somename')
366 assert 'Property not found' in str(exinfo.value)