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