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 crl.remotesession.remotesession import RemoteSession
18 from crl.interactivesessions.shells.bashshell import BashShell
19 from hostcli import HostCli
20 from .envcreator import EnvCreator
21 from .usermanager import UserManager
22 from .metasingleton import MetaSingleton
31 LOGGER = logging.getLogger(__name__)
34 class ClusterError(Exception):
38 @six.add_metaclass(MetaSingleton)
39 class Cluster(object):
40 """Singleton container for NCIR cluster hosts, their service profiles and
44 self._mgmt_target = None
46 self._configprops_cache = None
47 self._usermanager = UserManager(hostcli_factory=self.create_hostcli)
48 self._envcreator = EnvCreator(remotesession_factory=self.create_remotesession,
49 usermanager=self._usermanager)
51 def clear_cache(self):
52 """Clears configprops cache."""
53 self._configprops_cache = None
56 """Return Host containers of cluster.
58 return [h for _, h in self._hosts.items()]
60 def initialize(self, host, user, password):
61 """Initialize Cluster with management VIP
64 host: Management VIP IP address
66 password: password for the user
68 self._mgmt_target = MgmtTarget(host=host, user=user, password=password)
69 self._hosts = {hc.name: self._create_host(hc) for hc in self._host_configs()}
72 def set_profiles(master, worker):
73 """Set *service_profile' names of *master* and *worker* nodes
75 Profiles.set_profiles(master=master, worker=worker)
77 def get_mgmt_shelldicts(self):
78 """Return management VIP shelldicts for RemoteRunner.
80 return [self._mgmt_target.asdict()]
82 def get_host(self, hostname):
83 """Get Host container for hostname."""
84 return self._hosts[hostname]
86 def get_hosts_with_profiles(self, *service_profiles):
87 """Get host names matching exactly *service_profiles*.
89 return sorted(list(self._get_hosts_with_profs_gen(set(service_profiles))))
91 def get_hosts_containing(self, *service_profiles):
92 """Get host names containing all *service_profiles*.
94 return sorted(list(self._get_hosts_containing_gen(set(service_profiles))))
96 def create_remotesession(self):
97 """Create initialized *RemoteSession* instance.
100 self.initialize_remotesession(r)
103 def initialize_remotesession(self, remotesession):
104 """Initialize :class:`crl.remotesession.remotesession.RemoteSession` instance
105 with *shelldicts* and *name* of the hosts and sudo-<name> via *set_runner_target*.
106 Initialize *default* target with *get_mgmt_shelldicts* return value.
107 The sudo-<name> targets are terminals in target after executed roughly *sudo bash*.
109 self._set_mgmt_targets(remotesession)
110 for host in self.get_hosts():
111 remotesession.set_runner_target(shelldicts=host.shelldicts,
113 remotesession.set_runner_target(
114 shelldicts=self._get_sudoshelldicts(host.shelldicts),
115 name='sudo-{}'.format(host.name))
117 remotesession.set_envcreator(self._envcreator)
119 def _set_mgmt_targets(self, remotesession):
120 remotesession.set_runner_target(self.get_mgmt_shelldicts())
121 remotesession.set_runner_target(
122 shelldicts=self._get_sudoshelldicts(self.get_mgmt_shelldicts()),
124 remotesession.set_target(host=self._mgmt_target.host,
125 username=self._mgmt_target.user,
126 password=self._mgmt_target.password,
127 name='remotescript-default')
130 def _get_sudoshelldicts(shelldicts):
131 return shelldicts + [{'shellname': BashShell.__name__,
134 def create_hostcli(self):
135 """Create initialized *HostCli* instance.
138 self.initialize_hostcli(n)
141 def initialize_hostcli(self, hostcli):
142 """Initialize :class:`crl.hostcli.HostCli` instance
143 (or :class:`crl.hostcli.OpenStack`) with
144 :class:`crl.remotesession.remotesession.RemoteSession` instance
145 initialized with :meth:`.initialize_remotesession`.
147 hostcli.initialize(self.create_remotesession())
149 def create_user_with_roles(self, *roles):
150 """Create according to roles list.
154 all_roles: all roles in the system
155 no_roles: empty role list
158 UserRecord of created user
160 return self._usermanager.create_user_with_roles(*roles)
162 def delete_users(self):
163 """Delete all users created by *Cluster*.
165 self._usermanager.delete_users()
168 """Return *True* if *dpdk* is used in provider network interfaces.
169 More detail, *True* if and only if there is at least one host with
170 network profile providing *ovs-dpdk* type interface.
172 for _, h in self._hosts.items():
177 def get_hosts_with_dpdk(self):
178 """Returns list of hosts where *dpdk* is in use.
179 In more detail, return sorted list of host names in which there is
180 at least one network profile containing *dpdk* type interface.
182 return sorted([h.name for _, h in self._hosts.items() if h.is_dpdk])
184 def _get_hosts_with_profs_gen(self, service_profiles):
186 mask = Profiles().profiles_mask
187 return not (service_profiles ^ set(host.service_profiles)) & mask
189 return self._filtered_hostnames(filt)
191 def _get_hosts_containing_gen(self, service_profiles):
193 return service_profiles.issubset(set(host.service_profiles))
195 return self._filtered_hostnames(filt)
197 def _filtered_hostnames(self, filt):
198 for h in self.get_hosts():
203 def _create_host(host_config):
204 LOGGER.debug('host_config: %s', host_config)
205 master = Profiles().master
206 return (Master(host_config)
207 if master in host_config.service_profiles else
208 NonMaster(host_config))
210 def _host_configs(self):
211 LOGGER.debug('cloud_hosts: %s', self._cloud_hosts)
212 for hostname, v in self._cloud_hosts.items():
213 yield HostConfig(name=hostname,
214 network_domain=v['network_domain'],
215 service_profiles=v['service_profiles'],
216 networking=self._get_networking(hostname),
217 mgmt_target=self._mgmt_target,
218 is_dpdk=self._is_host_dpdk(v))
220 def _is_host_dpdk(self, host_prop):
221 network_profiles = set(host_prop['network_profiles'])
222 return bool(network_profiles.intersection(set(self._dpdk_profiles())))
224 def _dpdk_profiles(self):
225 profiles = self._get_value_for_prop('cloud.network_profiles')
226 for prof_n, prof_v in profiles.items():
227 ifaces = prof_v.get('provider_network_interfaces', {})
228 for _, iface_v in ifaces.items():
229 if iface_v['type'] == 'ovs-dpdk':
233 def _cloud_hosts(self):
234 return self._get_value_for_prop('cloud.hosts')
236 def _get_networking(self, hostname):
237 return self._get_value_for_prop('{}.networking'.format(hostname))
239 def _get_value_for_prop(self, prop):
240 for pv in self._configprops:
241 if pv['property'] == prop:
243 raise ClusterError('Property not found: {}'.format(prop))
246 def _configprops(self):
247 if self._configprops_cache is None:
248 self._setup_configprops_cache()
249 return self._configprops_cache
251 def _setup_configprops_cache(self):
253 s.set_runner_target(self.get_mgmt_shelldicts())
256 self._configprops_cache = n.run('config-manager list properties')