Merge "FIX: Allow configuration of IPMI privilege level"
[ta/config-manager.git] / cmdatahandlers / src / cmdatahandlers / hosts / config.py
1 # Copyright 2019 Nokia
2
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
6 #
7 #     http://www.apache.org/licenses/LICENSE-2.0
8 #
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.
14
15 import re
16
17 from cmdatahandlers.api import configerror
18 from cmdatahandlers.api import config
19 from cmdatahandlers.api import utils
20 from serviceprofiles import profiles
21
22 VNF_EMBEDDED_RESERVED_MEMORY = "512Mi"
23 DUAL_VIM_CONTROLLER_RESERVED_MEMORY = "64Gi"
24 DUAL_VIM_DEFAULT_RESERVED_MEMORY = "32Gi"
25 MIDDLEWARE_RESERVED_MEMORY = "12Gi"
26
27 class Config(config.Config):
28     def __init__(self, confman):
29         super(Config, self).__init__(confman)
30         self.ROOT = 'cloud.hosts'
31         self.DOMAIN = 'hosts'
32         try:
33             self.update_service_profiles()
34         except Exception:
35             pass
36
37     def init(self):
38         pass
39
40     def validate(self):
41         hosts = []
42         try:
43             hosts = self.get_hosts()
44         except configerror.ConfigError:
45             pass
46
47         if hosts:
48             utils.validate_list_items_unique(hosts)
49
50         for host in hosts:
51             self._validate_host(host)
52
53     def mask_sensitive_data(self):
54         for hostname in self.config[self.ROOT].keys():
55             self.config[self.ROOT][hostname]['hwmgmt']['password'] = self.MASK
56             self.config[self.ROOT][hostname]['hwmgmt']['snmpv2_trap_community_string'] = self.MASK
57             self.config[self.ROOT][hostname]['hwmgmt']['snmpv3_authpass'] = self.MASK
58             self.config[self.ROOT][hostname]['hwmgmt']['snmpv3_privpass'] = self.MASK
59
60     def _validate_host(self, hostname):
61         self._validate_hwmgmt(hostname)
62         self._validate_service_profiles(hostname)
63         self._validate_network_profiles(hostname)
64         self._validate_performance_profiles(hostname)
65         self._validate_storage_profiles(hostname)
66
67     def _validate_hwmgmt(self, hostname):
68         ip = self.get_hwmgmt_ip(hostname)
69         utils.validate_ipv4_address(ip)
70         self.get_hwmgmt_user(hostname)
71         self.get_hwmgmt_password(hostname)
72         netconf = self.confman.get_networking_config_handler()
73
74         hwmgmtnet = None
75         try:
76             hwmgmtnet = netconf.get_hwmgmt_network_name()
77         except configerror.ConfigError:
78             pass
79
80         if hwmgmtnet:
81             domain = self.get_host_network_domain(hostname)
82             cidr = netconf.get_network_cidr(hwmgmtnet, domain)
83             utils.validate_ip_in_network(ip, cidr)
84
85     def get_hwmgmt_priv_level(self, hostname):
86         """get the hwmgmt IPMI privilege level.  Defaults to ADMINISTRATOR
87
88            Arguments:
89
90            hostname: The name of the node
91
92            Return:
93
94            The prvilege level, or ADMINISTRATOR if unspecified
95
96            Raise:
97
98            ConfigError in-case of an error
99         """
100         self._validate_hostname(hostname)
101
102         if 'hwmgmt' not in self.config[self.ROOT][hostname]:
103             raise configerror.ConfigError('No hwmgmt info defined for host')
104
105         return self.config[self.ROOT][hostname]['hwmgmt'].get('priv_level', 'ADMINISTRATOR')
106
107     def _validate_service_profiles(self, hostname):
108         node_profiles = self.get_service_profiles(hostname)
109         utils.validate_list_items_unique(node_profiles)
110         service_profiles_lib = profiles.Profiles()
111         serviceprofiles = service_profiles_lib.get_service_profiles()
112         for profile in node_profiles:
113             if profile not in serviceprofiles:
114                 raise configerror.ConfigError('Invalid service profile %s specified for host %s' % (profile, hostname))
115
116     def _validate_network_profiles(self, hostname):
117         node_profiles = self.get_network_profiles(hostname)
118         utils.validate_list_items_unique(profiles)
119         netprofconf = self.confman.get_network_profiles_config_handler()
120         netprofiles = netprofconf.get_network_profiles()
121         for profile in node_profiles:
122             if profile not in netprofiles:
123                 raise configerror.ConfigError('Invalid network profile %s specified for host %s' % (profile, hostname))
124
125     def _validate_performance_profiles(self, hostname):
126         node_performance_profiles = []
127         try:
128             node_performance_profiles = self.get_performance_profiles(hostname)
129         except configerror.ConfigError:
130             pass
131
132         if node_performance_profiles:
133             utils.validate_list_items_unique(node_performance_profiles)
134             perfprofconf = self.confman.get_performance_profiles_config_handler()
135             perfprofiles = perfprofconf.get_performance_profiles()
136             for profile in node_performance_profiles:
137                 if profile not in perfprofiles:
138                     raise configerror.ConfigError('Invalid performance profile %s specified for host %s' % (profile, hostname))
139
140     def _validate_storage_profiles(self, hostname):
141         node_storage_profiles = []
142         try:
143             node_storage_profiles = self.get_storage_profiles(hostname)
144         except configerror.ConfigError:
145             pass
146
147         if node_storage_profiles:
148             utils.validate_list_items_unique(node_storage_profiles)
149             storageprofconf = self.confman.get_storage_profiles_config_handler()
150             storageprofiles = storageprofconf.get_storage_profiles()
151             for profile in node_storage_profiles:
152                 if profile not in storageprofiles:
153                     raise configerror.ConfigError('Invalid storage profile %s specific for %s' % (profile, hostname))
154
155     def get_hosts(self):
156         """ get the list of hosts in the cloud
157
158             Return:
159
160             A sorted list of host names
161
162             Raise:
163
164             ConfigError in-case of an error
165         """
166         self.validate_root()
167
168         return sorted(self.config[self.ROOT].keys())
169
170     def get_labels(self, hostname):
171         noderole_label = "node-role.kubernetes.io/{}".format(self.get_noderole(hostname))
172         mandatory_labels = \
173             {"nodetype": self.get_nodetype(hostname),
174              "nodeindex": self.get_nodeindex(hostname),
175              "nodename": self.get_nodename(hostname),
176              noderole_label: ""}
177         labels = self.config[self.ROOT][hostname].get('labels', {}).copy()
178         labels.update(mandatory_labels)
179
180         if self.is_sriov_enabled(hostname):
181             labels.update({"sriov": "enabled"})
182
183         black_list = ['name']
184         return {name: attributes
185                 for name, attributes in labels.iteritems()
186                 if name not in black_list}
187
188     def get_nodetype(self, hostname):
189         service_profiles_lib = profiles.Profiles()
190         service_profiles = self.get_service_profiles(hostname)
191
192         if service_profiles_lib.get_caasmaster_service_profile() in service_profiles:
193             return service_profiles_lib.get_caasmaster_service_profile()
194         if service_profiles_lib.get_caasworker_service_profile() in service_profiles:
195             return service_profiles_lib.get_caasworker_service_profile()
196
197         return service_profiles[0]
198
199     def get_nodeindex(self, hostname):
200         return re.search(r'[-_](\d+)$', hostname).group(1)
201
202     def get_nodename(self, hostname):
203         return "{}{}".format(self.get_nodetype(hostname), self.get_nodeindex(hostname))
204
205     def get_noderole(self, hostname):
206         service_profiles_lib = profiles.Profiles()
207         service_profiles = self.get_service_profiles(hostname)
208
209         if service_profiles_lib.get_caasmaster_service_profile() in service_profiles:
210             return "master"
211         return "worker"
212
213     def is_sriov_enabled(self, hostname):
214         netprofs = self.get_network_profiles(hostname)
215         netprofconf = self.confman.get_network_profiles_config_handler()
216         for netprof in netprofs:
217             if 'sriov_provider_networks' in self.config[netprofconf.ROOT][netprof]:
218                 return True
219         return False
220
221     def get_enabled_hosts(self):
222         """ get the list of enabled hosts in the cloud
223
224             Return:
225
226             A list of host names
227
228             Raise:
229
230             ConfigError in-case of an error
231         """
232         self.validate_root()
233         hosts = self.get_hosts()
234         ret = []
235         for host in hosts:
236             if self.is_host_enabled(host):
237                 ret.append(host)
238         return ret
239
240     def get_hwmgmt_ip(self, hostname):
241         """get the hwmgmt ip address
242
243             Arguments:
244
245             hostname: The name of the node
246
247             Return:
248
249             The BMC ip address as a string
250
251             Raise:
252
253             ConfigError in-case of an error
254         """
255         self._validate_hostname(hostname)
256
257         if 'hwmgmt' not in self.config[self.ROOT][hostname] or 'address' not in self.config[self.ROOT][hostname]['hwmgmt']:
258             raise configerror.ConfigError('No hwmgmt info defined for host')
259
260         return self.config[self.ROOT][hostname]['hwmgmt']['address']
261
262     def get_hwmgmt_user(self, hostname):
263         """get the hwmgmt user
264
265             Arguments:
266
267             hostname: The name of the node
268
269             Return:
270
271             The BMC user name.
272
273             Raise:
274
275             ConfigError in-case of an error
276         """
277         self._validate_hostname(hostname)
278
279         if 'hwmgmt' not in self.config[self.ROOT][hostname] or 'user' not in self.config[self.ROOT][hostname]['hwmgmt']:
280             raise configerror.ConfigError('No hwmgmt info defined for host')
281
282         return self.config[self.ROOT][hostname]['hwmgmt']['user']
283
284     def get_hwmgmt_password(self, hostname):
285         """get the hwmgmt password
286
287            Arguments:
288
289            hostname: The name of the node
290
291            Return:
292
293            The BMC password
294
295            Raise:
296
297            ConfigError in-case of an error
298         """
299         self._validate_hostname(hostname)
300
301         if 'hwmgmt' not in self.config[self.ROOT][hostname] or 'password' not in self.config[self.ROOT][hostname]['hwmgmt']:
302             raise configerror.ConfigError('No hwmgmt info defined for host')
303
304         return self.config[self.ROOT][hostname]['hwmgmt']['password']
305
306     def get_service_profiles(self, hostname):
307         """get the node service profiles
308
309            Arguments:
310
311            hostname: The name of the node
312
313            Return:
314
315            A list containing service profile names
316
317            Raise:
318
319            ConfigError in-case of an error
320         """
321         self._validate_hostname(hostname)
322
323         if 'service_profiles' not in self.config[self.ROOT][hostname]:
324             raise configerror.ConfigError('No service profiles found')
325
326         return self.config[self.ROOT][hostname]['service_profiles']
327
328     def get_performance_profiles(self, hostname):
329         """ get the performance profiles
330
331             Arguments:
332
333             hostname: The name of the node
334
335             Return:
336
337             A list containing the perfromance profile names.
338
339             Raise:
340
341             ConfigError in-case of an error
342         """
343         self._validate_hostname(hostname)
344
345         if 'performance_profiles' not in self.config[self.ROOT][hostname]:
346             raise configerror.ConfigError('No performance profiles found')
347
348         return self.config[self.ROOT][hostname]['performance_profiles']
349
350     def get_network_profiles(self, hostname):
351         """get the node network profiles
352
353            Arguments:
354
355            hostname: The name of the node
356
357            Return:
358
359            A list containing network profile names
360
361            Raise:
362
363            ConfigError in-case of an error
364         """
365         self._validate_hostname(hostname)
366
367         if 'network_profiles' not in self.config[self.ROOT][hostname]:
368             raise configerror.ConfigError('No network profiles found')
369
370         return self.config[self.ROOT][hostname]['network_profiles']
371
372     def get_storage_profiles(self, hostname):
373         """get the node storage profiles
374
375            Arguments:
376
377            hostname: The name of the node
378
379            Return:
380
381            A list containing storage profile names
382
383            Raise:
384
385            ConfigError in-case of an error
386         """
387         self._validate_hostname(hostname)
388
389         if 'storage_profiles' not in self.config[self.ROOT][hostname]:
390             raise configerror.ConfigError('No storage profiles found')
391
392         return self.config[self.ROOT][hostname]['storage_profiles']
393
394     def _validate_hostname(self, hostname):
395         if not self.is_valid_host(hostname):
396             raise configerror.ConfigError('Invalid hostname given %s' % hostname)
397
398     def is_valid_host(self, hostname):
399         """check if a host is valid
400
401            Arguments:
402
403            hostname: The name of the node
404
405            Return:
406
407            True or False
408
409            Raise:
410
411            ConfigError in-case of an error
412         """
413         self.validate_root()
414         if hostname in self.config[self.ROOT]:
415             return True
416         return False
417
418     def get_service_profile_hosts(self, profile):
419         """ get hosts having some service profile
420
421             Argument:
422
423             service profile name
424
425             Return:
426
427             A list of host names
428
429             Raise:
430
431             ConfigError in-case of an error
432         """
433         hosts = self.get_hosts()
434         result = []
435         for host in hosts:
436             node_profiles = self.get_service_profiles(host)
437             if profile in node_profiles:
438                 result.append(host)
439
440         return result
441
442     def get_network_profile_hosts(self, profile):
443         """ get hosts having some network profile
444
445             Argument:
446
447             network profile name
448
449             Return:
450
451             A list of host names
452
453             Raise:
454
455             ConfigError in-case of an error
456         """
457         hosts = self.get_hosts()
458         result = []
459         for host in hosts:
460             node_network_profiles = self.get_network_profiles(host)
461             if profile in node_network_profiles:
462                 result.append(host)
463         if not result:
464             raise configerror.ConfigError('No hosts found for profile %s' % profile)
465
466         return result
467
468     def get_performance_profile_hosts(self, profile):
469         """ get hosts having some performance profile
470
471             Argument:
472
473             performance profile name
474
475             Return:
476
477             A list of host names
478
479             Raise:
480
481             ConfigError in-case of an error
482         """
483         hosts = self.get_hosts()
484         result = []
485         for host in hosts:
486             node_performance_profiles = self.get_performance_profiles(host)
487             if profile in node_performance_profiles:
488                 result.append(host)
489         if not result:
490             raise configerror.ConfigError('No hosts found for profile %s' % profile)
491
492         return result
493
494     def get_storage_profile_hosts(self, profile):
495         """ get hosts having some storage profile
496
497             Argument:
498
499             storage profile name
500
501             Return:
502
503             A list of host names
504
505             Raise:
506
507             ConfigError in-case of an error
508         """
509         hosts = self.get_hosts()
510         result = []
511         for host in hosts:
512             try:
513                 node_storage_profiles = self.get_storage_profiles(host)
514                 if profile in node_storage_profiles:
515                     result.append(host)
516             except configerror.ConfigError:
517                 pass
518
519         if not result:
520             raise configerror.ConfigError('No hosts found for profile %s' % profile)
521
522         return result
523
524     def get_host_network_interface(self, host, network):
525         """ get the host interface used for some network
526
527             Argument:
528
529             the host name
530
531             the network name
532
533             Return:
534
535             The interface name
536
537             Raise:
538
539             ConfigError in-case of an error
540         """
541         node_network_profiles = self.get_network_profiles(host)
542         netprofconf = self.confman.get_network_profiles_config_handler()
543         for profile in node_network_profiles:
544             interfaces = netprofconf.get_profile_network_mapped_interfaces(profile)
545             for interface in interfaces:
546                 networks = netprofconf.get_profile_interface_mapped_networks(profile, interface)
547                 if network in networks:
548                     return interface
549
550         raise configerror.ConfigError('No interfaces found for network %s in host %s' % (network, host))
551
552     def get_host_network_ip_holding_interface(self, host, network):
553         """ get the host ip holding interface some network
554
555             Argument:
556
557             the host name
558
559             the network name
560
561             Return:
562
563             The interface name
564
565             Raise:
566
567             ConfigError in-case of an error
568         """
569         networkingconf = self.confman.get_networking_config_handler()
570         vlan = None
571         try:
572             domain = self.get_host_network_domain(host)
573             vlan = networkingconf.get_network_vlan_id(network, domain)
574         except configerror.ConfigError as exp:
575             pass
576
577         if vlan:
578             return 'vlan'+str(vlan)
579
580         return self.get_host_network_interface(host, network)
581
582     def get_host_networks(self, hostname):
583         """ get the host networks
584
585             Argument:
586
587             The host name
588
589             Return:
590
591             A list of network names
592
593             Raise:
594
595             ConfigError in-case of an error
596         """
597         node_network_profiles = self.get_network_profiles(hostname)
598         netprofconf = self.confman.get_network_profiles_config_handler()
599         result = []
600         for profile in node_network_profiles:
601             interfaces = netprofconf.get_profile_network_mapped_interfaces(profile)
602             for interface in interfaces:
603                 networks = netprofconf.get_profile_interface_mapped_networks(profile, interface)
604                 for network in networks:
605                     if network not in result:
606                         result.append(network)
607         if not result:
608             raise configerror.ConfigError('No networks found for host %s' % hostname)
609
610         return result
611
612     def get_host_having_hwmgmt_address(self, hwmgmtips):
613         """ get the node name matching an ipmi address
614
615             Argument:
616
617             The ipmi address
618
619             Return:
620
621             The node name
622
623             Raise:
624
625             ConfigError in-case of an error
626         """
627         import ipaddress
628         hosts = self.get_hosts()
629         for host in hosts:
630             ip = self.get_hwmgmt_ip(host)
631             for hwmgtip in hwmgmtips:
632                 addr=ipaddress.ip_address(unicode(hwmgtip))
633                 if addr.version == 6:
634                    hwmgtip=addr.compressed
635                    ip=ipaddress.ip_address(unicode(ip))
636                    ip=ip.compressed
637                 if ip == hwmgtip:
638                    return host
639         raise configerror.ConfigError('No hosts are matching the provided hw address %s' % hwmgmtips)
640
641     def set_installation_host(self, name):
642         """ set the installation node
643
644             Argument:
645
646             The installation node name
647
648             Raise:
649
650             ConfigError in-case of an error
651         """
652         self._validate_hostname(name)
653
654         self.config[self.ROOT][name]['installation_host'] = True
655
656     def is_installation_host(self, name):
657         """ get if the node is an installation node
658
659             Argument:
660
661             The node name
662
663             Return:
664
665             True if installation node
666
667             Raise:
668
669             ConfigError in-case of an error
670         """
671         self._validate_hostname(name)
672
673         if 'installation_host' in self.config[self.ROOT][name]:
674             return self.config[self.ROOT][name]['installation_host']
675
676         return False
677
678     def get_installation_host(self):
679         """ get the name of the node used for installation
680
681             Return:
682
683             The node name
684
685             Raise:
686
687             ConfigError in-case of an error
688         """
689         hosts = self.get_hosts()
690         for host in hosts:
691             if self.is_installation_host(host):
692                 return host
693
694         raise configerror.ConfigError('No installation host found')
695
696     def disable_host(self, host):
697         """ disable the hosts visible via configuration.
698             This can be used in bootstrapping phase.
699
700             Argument:
701
702             host to disable
703
704             Raise:
705
706             ConfigError in-case if provided host is not valid
707         """
708         self._validate_hostname(host)
709
710         self.config[self.ROOT][host]['disabled'] = True
711
712     def enable_host(self, host):
713         """ enable  the hosts visible via configuration.
714             This can be used in bootstrapping phase.
715
716             Argument:
717
718             host to enable
719
720             Raise:
721
722             ConfigError in-case if provided host is not valid
723         """
724         self._validate_hostname(host)
725
726         self.config[self.ROOT][host]['disabled'] = False
727
728     def is_host_enabled(self, host):
729         """ is the host enabled
730
731             Argument:
732
733             the host to be checked
734
735             Raise:
736
737             ConfigError in-case if provided host is not valid
738         """
739         self._validate_hostname(host)
740
741         if 'disabled' in self.config[self.ROOT][host]:
742             return not self.config[self.ROOT][host]['disabled']
743
744         return True
745
746     def get_mgmt_mac(self, host):
747         self._validate_hostname(host)
748
749         if 'mgmt_mac' in self.config[self.ROOT][host]:
750             return self.config[self.ROOT][host]['mgmt_mac']
751         return []
752
753     def get_host_network_domain(self, host):
754         self._validate_hostname(host)
755         if 'network_domain' not in self.config[self.ROOT][host]:
756             raise configerror.ConfigError('Missing network domain for host %s' % host)
757         return self.config[self.ROOT][host]['network_domain']
758
759     def get_controllers_network_domain(self):
760         controllers = self.get_service_profile_hosts('controller')
761         domains = set()
762         for controller in controllers:
763             domains.add(self.get_host_network_domain(controller))
764
765         if len(domains) != 1:
766             raise configerror.ConfigError('Controllers in different networking domains not supported')
767         return domains.pop()
768
769     def get_managements_network_domain(self):
770         managements = self.get_service_profile_hosts('management')
771         domains = set()
772         for management in managements:
773             domains.add(self.get_host_network_domain(management))
774         if len(domains) != 1:
775             raise configerror.ConfigError('Management in different networking domains not supported')
776         return domains.pop()
777
778     def update_service_profiles(self):
779         profs = profiles.Profiles()
780         hosts = self.get_hosts()
781         for host in hosts:
782             new_profiles = []
783             current_profiles = self.config[self.ROOT][host]['service_profiles']
784             new_profiles = current_profiles
785             for profile in current_profiles:
786                 included_profiles = profs.get_included_profiles(profile)
787                 new_profiles = utils.add_lists(new_profiles, included_profiles)
788             self.config[self.ROOT][host]['service_profiles'] = new_profiles
789
790     def get_pre_allocated_ips(self, host, network):
791         ips_field = "pre_allocated_ips"
792         self._validate_hostname(host)
793         if (ips_field not in self.config[self.ROOT][host]
794                 or network not in self.config[self.ROOT][host][ips_field]):
795             return None
796         return self.config[self.ROOT][host][ips_field][network]
797
798     def allocate_port(self, host, base, name):
799         used_ports = []
800         hosts = self.get_hosts()
801         for node in hosts:
802             if name in self.config[self.ROOT][node]:
803                 used_ports.append(self.config[self.ROOT][node][name])
804
805         free_port = 0
806
807         for port in range(base, base+1000):
808             if port not in used_ports:
809                 free_port = port
810                 break
811
812         if free_port == 0:
813             raise configerror.ConfigError('No free ports available')
814
815         self.config[self.ROOT][host][name] = free_port
816
817     def add_vbmc_port(self, host):
818         base_vbmc_port = 61600
819         name = 'vbmc_port'
820         self._validate_hostname(host)
821         if name not in self.config[self.ROOT][host]:
822             self.allocate_port(host, base_vbmc_port, name)
823
824     def add_ipmi_terminal_port(self, host):
825         base_console_port = 61401
826         name = 'ipmi_terminal_port'
827         self._validate_hostname(host)
828         if name not in self.config[self.ROOT][host]:
829             self.allocate_port(host, base_console_port, name)
830
831     def get_ceph_osd_disks(self, host):
832         self._validate_hostname(host)
833         caas_disks = self.config[self.ROOT][host].get('caas_disks', [])
834         osd_disks = filter(lambda disk: disk.get('osd_disk', False), caas_disks)
835         return map(lambda disk: _get_path_for_virtio_id(disk), osd_disks)
836
837     def get_system_reserved_memory(self, hostname):
838         caasconf = self.confman.get_caas_config_handler()
839         if caasconf.is_vnf_embedded_deployment():
840             return VNF_EMBEDDED_RESERVED_MEMORY
841         
842         profiles = self.get_service_profiles(hostname)
843         if 'controller' in profiles:
844             return DUAL_VIM_CONTROLLER_RESERVED_MEMORY
845         if 'compute' in profiles:
846             return DUAL_VIM_DEFAULT_RESERVED_MEMORY
847
848         return self.config.get(self.ROOT, {}).get('middleware_reserved_memory', 
849                                                    MIDDLEWARE_RESERVED_MEMORY)
850
851     def set_default_reserved_memory_to_all_hosts(self, def_memory):
852         for host in self.get_hosts():
853             self.config[self.ROOT][host]['middleware_reserved_memory'] = def_memory
854
855
856 def _get_path_for_virtio_id(disk):
857     disk_id = disk.get('id', '')
858     if disk_id:
859         return "/dev/disk/by-id/virtio-{}".format(disk_id[:20])
860
861