X-Git-Url: https://gerrit.akraino.org/r/gitweb?a=blobdiff_plain;f=validators%2Fsrc%2FCaasValidation.py;h=4bcee33fec24ac0ad79dd00e6cb30363b21a3ac0;hb=673e3c7ecfc99153fe8d423ded5dba368219a6ee;hp=e72a598d0bac668bb42d490e748629d419e4ffe8;hpb=b4c24eb199d07b2b896658a50de941168b7ae43b;p=ta%2Fcm-plugins.git diff --git a/validators/src/CaasValidation.py b/validators/src/CaasValidation.py index e72a598..4bcee33 100644 --- a/validators/src/CaasValidation.py +++ b/validators/src/CaasValidation.py @@ -84,6 +84,8 @@ class CaasValidation(cmvalidator.CMValidator): DOCKER0_CIDR = "docker0_cidr" + OAM_CIDR = "oam_cidr" + INSTANTIATION_TIMEOUT = "instantiation_timeout" ENCRYPTED_CA = "encrypted_ca" @@ -92,6 +94,17 @@ class CaasValidation(cmvalidator.CMValidator): CLUSTER_NETS = 'cluster_networks' TENANT_NETS = 'tenant_networks' + BLOG_FORWARDING = "infra_log_store" + LOG_FORWARDING = "log_forwarding" + URL_PORT_PATTERN = r"^(?:https?|udp|tcp):(?:\/\/)(?:((?:[\w\.-]+|" \ + r"\[(([1-9a-f][0-9a-f]{0,3}|\:)\:[1-9a-f][0-9a-f]{0,3}){0,7}\])\:[0-9]+))" + FLUENTD_PLUGINS = ['elasticsearch', 'remote_syslog'] + INFRA_LOG_FLUENTD_PLUGINS = ['elasticsearch', 'remote_syslog'] + LOG_FW_STREAM = ['stdout', 'stderr', 'both'] + + DOMAIN_NAME = "dns_domain" + DOMAIN_NAME_PATTERN = r"^[a-z0-9]([a-z0-9-\.]{0,253}[a-z0-9])?$" + def __init__(self): cmvalidator.CMValidator.__init__(self) self.validation_utils = validation.ValidationUtils() @@ -111,10 +124,13 @@ class CaasValidation(cmvalidator.CMValidator): self.validate_docker_size_quota() self.validate_helm_operation_timeout() self.validate_docker0_cidr(props) + self.validate_oam_cidr(props) self.validate_instantiation_timeout() self.validate_encrypted_ca(self.ENCRYPTED_CA) self.validate_encrypted_ca(self.ENCRYPTED_CA_KEY) + self.validate_log_forwarding() self.validate_networks(props) + self.validate_dns_domain() def _get_conf(self, props, domain): if props.get(domain): @@ -156,28 +172,39 @@ class CaasValidation(cmvalidator.CMValidator): '{}:{} is not an integer'.format(self.HELM_OP_TIMEOUT, self.caas_conf[self.HELM_OP_TIMEOUT])) - def get_docker0_cidr_netw_obj(self, subnet): + def get_netw_obj(self, subnet, parameter): try: return ipaddr.IPNetwork(subnet) except ValueError as exc: raise CaasValidationError('{} is an invalid subnet address: {}'.format( - self.DOCKER0_CIDR, exc)) + parameter, exc)) - def check_docker0_cidr_overlaps_with_netw_subnets(self, docker0_cidr, props): + def check_cidr_overlaps_with_netw_subnets(self, cidr_in, props, parameter): netw_conf = self._get_conf(props, self.NETW_DOMAIN) cidrs = self.caas_utils.get_every_key_occurrence(netw_conf, self.CIDR) - for cidr in cidrs: + for cidr_in in cidrs: if docker0_cidr.overlaps(ipaddr.IPNetwork(cidr)): raise CaasValidationError( 'CIDR configured for {} shall be an unused IP range, ' - 'but it overlaps with {} from {}.'.format(self.DOCKER0_CIDR, cidr, + 'but it overlaps with {} from {}.'.format(parameter, cidr, self.NETW_DOMAIN)) + def check_oam_cidr_prefix(self, cidr_obj): + if ipaddr.IPNetwork(cidr_obj).prefixlen != 16: + raise CaasValidationError('Wrong subnet size in caas.oam_cidr parameter. ' + 'The currently supported subnet size is 16') def validate_docker0_cidr(self, props): if not self.caas_utils.is_optional_param_present(self.DOCKER0_CIDR, self.caas_conf): return - docker0_cidr_obj = self.get_docker0_cidr_netw_obj(self.caas_conf[self.DOCKER0_CIDR]) - self.check_docker0_cidr_overlaps_with_netw_subnets(docker0_cidr_obj, props) + docker0_cidr_obj = self.get_netw_obj(self.caas_conf[self.DOCKER0_CIDR], self.DOCKER0_CIDR) + self.check_cidr_overlaps_with_netw_subnets(docker0_cidr_obj, props, self.DOCKER0_CIDR) + + def validate_oam_cidr(self, props): + if not self.caas_utils.is_optional_param_present(self.OAM_CIDR, self.caas_conf): + return + oam_cidr_obj = self.get_netw_obj(self.caas_conf[self.OAM_CIDR], self.OAM_CIDR) + self.check_cidr_overlaps_with_netw_subnets(oam_cidr_obj, props, self.OAM_CIDR) + self.check_oam_cidr_prefix(oam_cidr_obj) def validate_instantiation_timeout(self): if not self.caas_utils.is_optional_param_present(self.INSTANTIATION_TIMEOUT, @@ -197,6 +224,52 @@ class CaasValidation(cmvalidator.CMValidator): except TypeError as exc: raise CaasValidationError('Invalid {}: {}'.format(enc_ca, exc)) + def validate_log_forwarding(self): + # pylint: disable=too-many-branches + if self.caas_utils.is_optional_param_present(self.BLOG_FORWARDING, self.caas_conf): + if self.caas_conf[self.BLOG_FORWARDING] not in self.INFRA_LOG_FLUENTD_PLUGINS: + raise CaasValidationError('"{}" property not valid! ' + 'Choose from {}!'.format(self.BLOG_FORWARDING, + self.INFRA_LOG_FLUENTD_PLUGINS)) + if self.caas_utils.is_optional_param_present(self.LOG_FORWARDING, self.caas_conf): + log_fw_list = self.caas_conf[self.LOG_FORWARDING] + if log_fw_list: + url_d = dict() + url_s = set() + for list_item in log_fw_list: + self.caas_utils.check_key_in_dict('namespace', list_item) + if list_item['namespace'] == 'kube-system': + raise CaasValidationError( + 'You can\'t set "kube-system" as namespace in "{}"!'.format( + self.LOG_FORWARDING)) + self.caas_utils.check_key_in_dict('target_url', list_item) + if not list_item['target_url'] or not re.match(self.URL_PORT_PATTERN, + list_item['target_url']): + raise CaasValidationError( + '"target_url" property {} not valid!'.format(list_item['target_url'])) + if not url_d: + url_d[list_item['namespace']] = list_item['target_url'] + if list_item['namespace'] in url_d: + if list_item['target_url'] in url_s: + raise CaasValidationError('There can\'t be multiple rules for the same ' + 'target_url for the same {} ' + 'namespace!'.format(list_item['namespace'])) + else: + url_s.add(list_item['target_url']) + url_d[list_item['namespace']] = url_s + else: + url_d[list_item['namespace']] = list_item['target_url'] + if self.caas_utils.is_optional_param_present('plugin', list_item) and list_item[ + 'plugin'] not in self.FLUENTD_PLUGINS: + raise CaasValidationError( + '"plugin" property not valid! Choose from {}'.format( + self.FLUENTD_PLUGINS)) + if self.caas_utils.is_optional_param_present('stream', list_item) and list_item[ + 'stream'] not in self.LOG_FW_STREAM: + raise CaasValidationError( + '"stream" property not valid! Choose from {}'.format( + self.LOG_FW_STREAM)) + def validate_networks(self, props): caas_nets = [] for nets_key in [self.CLUSTER_NETS, self.TENANT_NETS]: @@ -226,25 +299,80 @@ class CaasValidation(cmvalidator.CMValidator): ('caas_master' in host_conf[self.SERV_PROF] and 'compute' not in host_conf[self.SERV_PROF])): # Validating CaaS network 'net' mapping in 'host' - is_caas_network_present = False profiles = host_conf.get('network_profiles') if isinstance(profiles, list) and profiles: net_prof = netprof_conf.get(profiles[0]) if net_prof is not None: ifaces = net_prof.get('provider_network_interfaces', {}) - for iface, data in ifaces.iteritems(): - net_type = data.get('type') - networks = data.get('provider_networks', []) - if net in networks and net_type == 'caas': - is_caas_network_present = True - if net_iface_map[net] is None: - net_iface_map[net] = iface - elif net_iface_map[net] != iface: - msg = 'CaaS network {} mapped to interface {} in one host ' - msg += 'and interface {} in another host' - raise CaasValidationError(msg.format(net, iface, - net_iface_map[net])) - break - if not is_caas_network_present: - raise CaasValidationError('CaaS network {} missing from host {}' - .format(net, host)) + caas_provider_interfaces = self._filter_provider_networks_by_type( + self._filter_provider_networkinterfaces_by_net(ifaces, net), 'caas') + sriov_networks = net_prof.get('sriov_provider_networks', {}) + caas_sriov_networks_present = bool( + net in sriov_networks and + sriov_networks[net].get('type', "") == 'caas') + if not caas_provider_interfaces and not caas_sriov_networks_present: + raise CaasValidationError('CaaS network {} missing from host {}' + .format(net, host)) + if caas_provider_interfaces: + self._validate_homogenous_provider_net_setup( + net_iface_map, net, ifaces) + if caas_sriov_networks_present: + self._validate_homogenous_sriov_provider_net_setup( + net_iface_map, net, sriov_networks) + + @staticmethod + def _filter_provider_networks_by_type(profile, net_type): + return {name: network for name, network in profile.iteritems() + if network.get('type', "") == net_type} + + @staticmethod + def _filter_provider_networkinterfaces_by_net(provider_interfaces, provider_net): + return {iface: data for iface, data in provider_interfaces.iteritems() + if provider_net in data.get('provider_networks', [])} + + @staticmethod + def _validate_homogenous_provider_net_setup(net_iface_map, net, ifaces): + is_caas_network_present = False + for iface, data in ifaces.iteritems(): + net_type = data.get('type') + networks = data.get('provider_networks', []) + if net in networks and net_type == 'caas': + is_caas_network_present = True + if net_iface_map[net] is None: + net_iface_map[net] = iface + elif net_iface_map[net] != iface: + msg = 'CaaS network {} mapped to interface {} in one host ' + msg += 'and interface {} in another host' + raise CaasValidationError(msg.format(net, iface, + net_iface_map[net])) + break + return is_caas_network_present + + @staticmethod + def _validate_homogenous_sriov_provider_net_setup(net_iface_map, net, sriov_networks): + is_caas_network_present = False + sriov_provider_net = sriov_networks.get(net, {}) + if sriov_provider_net and sriov_provider_net.get('type') == 'caas': + interfaces = sriov_provider_net.get('interfaces', []) + tenant_interfaces = net_iface_map.get(net) + already_used_ifaces = [set(x).intersection(interfaces) + for x in net_iface_map.itervalues() + if x and isinstance(x, list)] + if tenant_interfaces is None and interfaces: + net_iface_map[net] = interfaces + is_caas_network_present = True + elif already_used_ifaces: + msg = 'CaaS network {} mapped to sriov interfaces {} in one host ' + msg += 'and sriov interfaces {} in another host' + raise CaasValidationError(msg.format(net, interfaces, + tenant_interfaces)) + return is_caas_network_present + + def validate_dns_domain(self): + domain = self.caas_conf[self.DOMAIN_NAME] + if not self.caas_utils.is_optional_param_present(self.DOMAIN_NAME, self.caas_conf): + return + if not re.match(self.DOMAIN_NAME_PATTERN, domain): + raise CaasValidationError('{} is not a valid {} !'.format( + domain, + self.DOMAIN_NAME))