4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
8 # http://www.apache.org/licenses/LICENSE-2.0
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
18 from netaddr import IPNetwork
20 from cmdatahandlers.api import validation
21 from cmframework.apis import cmvalidator
24 class NetworkingValidation(cmvalidator.CMValidator):
25 SUBSCRIPTION = r'^cloud\.networking$'
26 DOMAIN = 'cloud.networking'
34 PROVNET_NAME_MATCH = r'^[a-zA-Z][\da-zA-Z-_]+[\da-zA-Z]$'
35 NET_DOMAIN_MATCH = PROVNET_NAME_MATCH
36 MAX_NET_DOMAIN_LEN = MAX_PROVNET_LEN
37 DEFAULT_ROUTE_DEST = '0.0.0.0/0'
39 NETWORK_DOMAINS = 'network_domains'
40 INFRA_EXTERNAL = 'infra_external'
41 INFRA_INTERNAL = 'infra_internal'
42 INFRA_STORAGE_CLUSTER = 'infra_storage_cluster'
44 INFRA_NETWORKS = [INFRA_EXTERNAL,
46 INFRA_STORAGE_CLUSTER,
54 IP_START = 'ip_range_start'
55 IP_END = 'ip_range_end'
60 PROVIDER_NETWORKS = 'provider_networks'
61 VLAN_RANGES = 'vlan_ranges'
64 INPUT_ERR_CONTEXT = 'validate_set() input'
65 ERR_INPUT_NOT_DICT = 'Invalid %s, not a dictionary' % INPUT_ERR_CONTEXT
67 ERR_MISSING = 'Missing {1} configuration in {0}'
68 ERR_NOT_DICT = 'Invalid {1} value in {0}: Empty or not a dictionary'
69 ERR_NOT_LIST = 'Invalid {1} value in {0}: Empty, contains duplicates or not a list'
70 ERR_NOT_STR = 'Invalid {1} value in {0}: Not a string'
71 ERR_NOT_INT = 'Invalid {1} value in {0}: Not an integer'
72 ERR_NOT_BOOL = 'Invalid {1} value in {0}: Not a boolean value'
74 ERR_MTU = 'Invalid {} mtu: Not in range %i - %i' % (MIN_MTU, MAX_MTU)
75 ERR_VLAN = 'Invalid {} vlan: Not in range %i - %i' % (MIN_VLAN, MAX_VLAN)
76 ERR_DUPLICATE_INFRA_VLAN = 'Same VLAN ID {} used for multiple infra networks'
77 ERR_CIDRS_OVERLAPPING = 'Network CIDR values {} and {} are overlapping'
78 ERR_GW_NOT_SUPPORTED = 'Gateway address not supported for {}'
79 ERR_INVALID_ROUTES = 'Invalid static routes format for {0} {1}'
80 ERR_DEFAULT_ROUTE = 'Default route not supported for {0} {1}'
82 ERR_VLAN_RANGES_FORMAT = 'Invalid {} vlan_ranges format'
83 ERR_VLAN_RANGES_OVERLAPPING = 'Provider network vlan ranges {} and {} are overlapping'
85 ERR_INVALID_PROVNET_NAME = 'Invalid provider network name'
86 ERR_PROVNET_LEN = 'Too long provider network name, max %s chars' % MAX_PROVNET_LEN
87 ERR_SHARED_NETWORKS = 'Only one provider network can be configured as shared'
89 ERR_INVALID_NET_DOMAIN_NAME = 'Invalid network domain name'
90 ERR_NET_DOMAIN_LEN = 'Too long network domain name, max %s chars' % MAX_NET_DOMAIN_LEN
92 ERR_TOO_MANY_DNS = 'Too many DNS server IP addresses, max %i supported' % MAX_DNS
94 ERR_MTU_INSIDE_NETWORK_DOMAIN = 'Missplaced MTU inside {} network domain {}'
97 def err_input_not_dict():
98 raise validation.ValidationError(NetworkingValidation.ERR_INPUT_NOT_DICT)
101 def err_missing(context, key):
102 raise validation.ValidationError(NetworkingValidation.ERR_MISSING.format(context, key))
105 def err_not_dict(context, key):
106 raise validation.ValidationError(NetworkingValidation.ERR_NOT_DICT.format(context, key))
109 def err_not_list(context, key):
110 raise validation.ValidationError(NetworkingValidation.ERR_NOT_LIST.format(context, key))
113 def err_not_str(context, key):
114 raise validation.ValidationError(NetworkingValidation.ERR_NOT_STR.format(context, key))
117 def err_not_int(context, key):
118 raise validation.ValidationError(NetworkingValidation.ERR_NOT_INT.format(context, key))
121 def err_not_bool(context, key):
122 raise validation.ValidationError(NetworkingValidation.ERR_NOT_BOOL.format(context, key))
125 def err_mtu(context):
126 raise validation.ValidationError(NetworkingValidation.ERR_MTU.format(context))
129 def err_vlan(context):
130 raise validation.ValidationError(NetworkingValidation.ERR_VLAN.format(context))
133 def err_duplicate_vlan(vid):
134 raise validation.ValidationError(NetworkingValidation.ERR_DUPLICATE_INFRA_VLAN.format(vid))
137 def err_vlan_ranges_format(provnet):
138 err = NetworkingValidation.ERR_VLAN_RANGES_FORMAT.format(provnet)
139 raise validation.ValidationError(err)
142 def err_vlan_ranges_overlapping(range1, range2):
143 ranges = sorted([range1, range2])
144 err = NetworkingValidation.ERR_VLAN_RANGES_OVERLAPPING.format(ranges[0], ranges[1])
145 raise validation.ValidationError(err)
148 def err_invalid_provnet_name():
149 raise validation.ValidationError(NetworkingValidation.ERR_INVALID_PROVNET_NAME)
152 def err_provnet_len():
153 raise validation.ValidationError(NetworkingValidation.ERR_PROVNET_LEN)
156 def err_invalid_net_domain_name():
157 raise validation.ValidationError(NetworkingValidation.ERR_INVALID_NET_DOMAIN_NAME)
160 def err_net_domain_len():
161 raise validation.ValidationError(NetworkingValidation.ERR_NET_DOMAIN_LEN)
164 def err_cidrs_overlapping(cidr1, cidr2):
165 cidrs = sorted([cidr1, cidr2])
166 err = NetworkingValidation.ERR_CIDRS_OVERLAPPING.format(cidrs[0], cidrs[1])
167 raise validation.ValidationError(err)
170 def err_gw_not_supported(network):
171 raise validation.ValidationError(NetworkingValidation.ERR_GW_NOT_SUPPORTED.format(network))
174 def err_invalid_routes(network, domain):
175 err = NetworkingValidation.ERR_INVALID_ROUTES.format(network, domain)
176 raise validation.ValidationError(err)
179 def err_default_route(network, domain):
180 err = NetworkingValidation.ERR_DEFAULT_ROUTE.format(network, domain)
181 raise validation.ValidationError(err)
184 def err_too_many_dns():
185 raise validation.ValidationError(NetworkingValidation.ERR_TOO_MANY_DNS)
188 def err_shared_networks():
189 raise validation.ValidationError(NetworkingValidation.ERR_SHARED_NETWORKS)
192 def err_mtu_inside_network_domain(infra, domain):
193 err = NetworkingValidation.ERR_MTU_INSIDE_NETWORK_DOMAIN.format(infra, domain)
194 raise validation.ValidationError(err)
198 return isinstance(conf, dict)
201 def key_exists(conf_dict, key):
202 return key in conf_dict
205 def val_is_str(conf_dict, key):
206 return isinstance(conf_dict[key], basestring)
209 def val_is_list(conf_dict, key):
210 return isinstance(conf_dict[key], list)
213 def val_is_non_empty_list(conf_dict, key):
214 return (isinstance(conf_dict[key], list) and
215 len(conf_dict[key]) > 0 and
216 len(conf_dict[key]) == len(set(conf_dict[key])))
219 def val_is_non_empty_dict(conf_dict, key):
220 return isinstance(conf_dict[key], dict) and len(conf_dict[key]) > 0
223 def val_is_int(conf_dict, key):
224 return isinstance(conf_dict[key], (int, long))
227 def val_is_bool(conf_dict, key):
228 return isinstance(conf_dict[key], bool)
231 def key_must_exist(conf_dict, entry, key):
232 if not NetworkingValidation.key_exists(conf_dict[entry], key):
233 NetworkingValidation.err_missing(entry, key)
236 def must_be_str(conf_dict, entry, key):
237 NetworkingValidation.key_must_exist(conf_dict, entry, key)
238 if not NetworkingValidation.val_is_str(conf_dict[entry], key):
239 NetworkingValidation.err_not_str(entry, key)
242 def must_be_list(conf_dict, entry, key):
243 NetworkingValidation.key_must_exist(conf_dict, entry, key)
244 if not NetworkingValidation.val_is_non_empty_list(conf_dict[entry], key):
245 NetworkingValidation.err_not_list(entry, key)
248 def must_be_dict(conf_dict, entry, key):
249 NetworkingValidation.key_must_exist(conf_dict, entry, key)
250 if not NetworkingValidation.val_is_non_empty_dict(conf_dict[entry], key):
251 NetworkingValidation.err_not_dict(entry, key)
254 def exists_as_dict(conf_dict, entry, key):
255 if not NetworkingValidation.key_exists(conf_dict[entry], key):
257 if not NetworkingValidation.val_is_non_empty_dict(conf_dict[entry], key):
258 NetworkingValidation.err_not_dict(entry, key)
262 def exists_as_int(conf_dict, entry, key):
263 if not NetworkingValidation.key_exists(conf_dict[entry], key):
265 if not NetworkingValidation.val_is_int(conf_dict[entry], key):
266 NetworkingValidation.err_not_int(entry, key)
270 def exists_as_bool(conf_dict, entry, key):
271 if not NetworkingValidation.key_exists(conf_dict[entry], key):
273 if not NetworkingValidation.val_is_bool(conf_dict[entry], key):
274 NetworkingValidation.err_not_bool(entry, key)
278 cmvalidator.CMValidator.__init__(self)
279 self.utils = validation.ValidationUtils()
283 def get_subscription_info(self):
284 return self.SUBSCRIPTION
286 def validate_set(self, props):
287 self.prepare_validate(props)
290 def prepare_validate(self, props):
291 if not self.is_dict(props):
292 self.err_input_not_dict()
294 if not self.key_exists(props, self.DOMAIN):
295 self.err_missing(self.INPUT_ERR_CONTEXT, self.DOMAIN)
297 self.net_conf = json.loads(props[self.DOMAIN])
298 self.conf = {self.DOMAIN: self.net_conf}
300 if not self.val_is_non_empty_dict(self.conf, self.DOMAIN):
301 self.err_not_dict(self.INPUT_ERR_CONTEXT, self.DOMAIN)
305 self.validate_default_mtu()
306 self.validate_infra_networks()
307 self.validate_provider_networks()
308 self.validate_no_overlapping_cidrs()
310 def validate_dns(self):
311 self.must_be_list(self.conf, self.DOMAIN, self.DNS)
312 for server in self.net_conf[self.DNS]:
313 self.utils.validate_ip_address(server)
314 if len(self.net_conf[self.DNS]) > self.MAX_DNS:
315 self.err_too_many_dns()
317 def validate_default_mtu(self):
318 self.validate_mtu(self.conf, self.DOMAIN)
320 def validate_infra_networks(self):
321 self.validate_infra_internal()
322 self.validate_infra_external()
323 self.validate_infra_storage_cluster()
324 self.validate_caas_oam()
325 self.validate_no_duplicate_infra_vlans()
327 def validate_infra_internal(self):
328 self.validate_network_exists(self.INFRA_INTERNAL)
329 self.validate_infra_network(self.INFRA_INTERNAL)
330 self.validate_no_gateway(self.INFRA_INTERNAL)
332 def validate_infra_external(self):
333 self.validate_network_exists(self.INFRA_EXTERNAL)
334 self.validate_infra_network(self.INFRA_EXTERNAL)
335 self.validate_gateway(self.INFRA_EXTERNAL)
337 def validate_infra_storage_cluster(self):
338 if self.network_exists(self.INFRA_STORAGE_CLUSTER):
339 self.validate_network_domains(self.INFRA_STORAGE_CLUSTER)
340 self.validate_infra_network(self.INFRA_STORAGE_CLUSTER)
341 self.validate_no_gateway(self.INFRA_STORAGE_CLUSTER)
343 def validate_caas_oam(self):
344 if self.network_exists(self.CAAS_OAM):
345 self.validate_infra_network(self.CAAS_OAM)
346 if self.gateway_exists(self.CAAS_OAM):
347 self.validate_gateway(self.CAAS_OAM)
349 def gateway_exists(self, network):
350 return self.exists_as_dict(self.conf[self.DOMAIN], network, 'gateway')
352 def validate_infra_network(self, network, vlan_must_exist=False):
353 self.validate_mtu(self.net_conf, network)
354 self.validate_cidr(network)
355 self.validate_vlan(network, vlan_must_exist)
356 self.validate_ip_range(network)
357 self.validate_routes(network)
358 self.validate_no_mtu_inside_network_domain(network)
360 def validate_no_duplicate_infra_vlans(self):
362 for network in self.INFRA_NETWORKS:
363 if self.key_exists(self.net_conf, network):
364 for domain, domain_conf in self.net_conf[network][self.NETWORK_DOMAINS].iteritems():
365 if self.key_exists(domain_conf, self.VLAN):
366 if domain not in domvids:
368 domvids[domain].append(domain_conf[self.VLAN])
369 for vids in domvids.itervalues():
371 for vid in sorted(vids):
373 self.err_duplicate_vlan(vid)
376 def validate_no_overlapping_cidrs(self):
378 for network in self.INFRA_NETWORKS:
379 if self.key_exists(self.net_conf, network):
380 for domain_conf in self.net_conf[network][self.NETWORK_DOMAINS].itervalues():
381 cidrs.append(IPNetwork(domain_conf[self.CIDR]))
382 for idx, cidr1 in enumerate(cidrs):
383 for cidr2 in cidrs[(idx+1):]:
384 if not (cidr1[0] > cidr2[-1] or cidr1[-1] < cidr2[0]):
385 self.err_cidrs_overlapping(str(cidr1), str(cidr2))
387 def validate_ip_range(self, network):
388 domains = self.net_conf[network][self.NETWORK_DOMAINS]
389 for domain in domains:
390 ip_start = self.get_ip_range_start(domains, domain)
391 ip_end = self.get_ip_range_end(domains, domain)
392 self.utils.validate_ip_range(ip_start, ip_end)
394 def get_ip_range_start(self, domains, domain):
395 if self.key_exists(domains[domain], self.IP_START):
396 self.validate_ip_range_limiter(domains, domain, self.IP_START)
397 return domains[domain][self.IP_START]
398 return str(IPNetwork(domains[domain][self.CIDR])[1])
400 def get_ip_range_end(self, domains, domain):
401 if self.key_exists(domains[domain], self.IP_END):
402 self.validate_ip_range_limiter(domains, domain, self.IP_END)
403 return domains[domain][self.IP_END]
404 return str(IPNetwork(domains[domain][self.CIDR])[-2])
406 def validate_ip_range_limiter(self, domains, domain, key):
407 self.must_be_str(domains, domain, key)
408 self.utils.validate_ip_address(domains[domain][key])
409 self.utils.validate_ip_in_subnet(domains[domain][key],
410 domains[domain][self.CIDR])
412 def validate_provider_networks(self):
413 if self.network_exists(self.PROVIDER_NETWORKS):
414 for netname in self.net_conf[self.PROVIDER_NETWORKS]:
415 self.validate_providernet(netname)
416 self.validate_shared_provider_network(self.net_conf[self.PROVIDER_NETWORKS])
418 def validate_providernet(self, netname):
419 self.validate_providernet_name(netname)
420 self.must_be_dict(self.net_conf, self.PROVIDER_NETWORKS, netname)
421 self.validate_mtu(self.net_conf[self.PROVIDER_NETWORKS], netname)
422 self.validate_vlan_ranges(self.net_conf[self.PROVIDER_NETWORKS], netname)
424 def validate_shared_provider_network(self, provider_conf):
426 for netname in provider_conf:
427 if self.exists_as_bool(provider_conf, netname, self.SHARED):
428 if provider_conf[netname][self.SHARED] is True:
430 if shared_counter > 1:
431 self.err_shared_networks()
433 def validate_mtu(self, conf, network):
434 if self.exists_as_int(conf, network, self.MTU):
435 mtu = conf[network][self.MTU]
436 if mtu < self.MIN_MTU or mtu > self.MAX_MTU:
437 self.err_mtu(network)
439 def validate_no_mtu_inside_network_domain(self, network):
440 domains = self.net_conf[network][self.NETWORK_DOMAINS]
441 for domain in domains:
442 if self.key_exists(domains[domain], self.MTU):
443 self.err_mtu_inside_network_domain(network, domain)
445 def validate_vlan(self, network, must_exist=False):
446 domains = self.net_conf[network][self.NETWORK_DOMAINS]
447 for domain in domains:
448 if must_exist and not self.key_exists(domains[domain], self.VLAN):
449 self.err_missing(network, self.VLAN)
450 if self.exists_as_int(domains, domain, self.VLAN):
451 self.validate_vlan_id(network, domains[domain][self.VLAN])
453 def validate_network_exists(self, network):
454 self.must_be_dict(self.conf, self.DOMAIN, network)
455 self.validate_network_domains(network)
457 def validate_network_domains(self, network):
458 self.must_be_dict(self.net_conf, network, self.NETWORK_DOMAINS)
459 for domain in self.net_conf[network][self.NETWORK_DOMAINS]:
460 self.validate_net_domain_name(domain)
462 def validate_net_domain_name(self, domain_name):
463 if (not isinstance(domain_name, basestring) or
464 not re.match(self.NET_DOMAIN_MATCH, domain_name)):
465 self.err_invalid_net_domain_name()
466 if len(domain_name) > self.MAX_NET_DOMAIN_LEN:
467 self.err_net_domain_len()
469 def network_exists(self, network):
470 return self.exists_as_dict(self.conf, self.DOMAIN, network)
472 def validate_cidr(self, network):
473 domains = self.net_conf[network][self.NETWORK_DOMAINS]
474 for domain in domains:
475 self.must_be_str(domains, domain, self.CIDR)
476 self.utils.validate_subnet_address(domains[domain][self.CIDR])
478 def validate_gateway(self, network):
479 domains = self.net_conf[network][self.NETWORK_DOMAINS]
480 for domain in domains:
481 self.must_be_str(domains, domain, self.GATEWAY)
482 self.utils.validate_ip_address(domains[domain][self.GATEWAY])
483 self.utils.validate_ip_in_subnet(domains[domain][self.GATEWAY],
484 domains[domain][self.CIDR])
485 self.utils.validate_ip_not_in_range(domains[domain][self.GATEWAY],
486 self.get_ip_range_start(domains, domain),
487 self.get_ip_range_end(domains, domain))
489 def validate_no_gateway(self, network):
490 for domain_conf in self.net_conf[network][self.NETWORK_DOMAINS].itervalues():
491 if self.key_exists(domain_conf, self.GATEWAY):
492 self.err_gw_not_supported(network)
494 def validate_routes(self, network):
495 domains = self.net_conf[network][self.NETWORK_DOMAINS]
496 for domain in domains:
497 if self.key_exists(domains[domain], self.ROUTES):
498 if (not self.val_is_list(domains[domain], self.ROUTES) or
499 not domains[domain][self.ROUTES]):
500 self.err_invalid_routes(network, domain)
501 for route in domains[domain][self.ROUTES]:
502 self.validate_route(network, domain, route)
503 self.utils.validate_ip_in_subnet(route[self.VIA],
504 domains[domain][self.CIDR])
505 self.utils.validate_ip_not_in_range(route[self.VIA],
506 self.get_ip_range_start(domains, domain),
507 self.get_ip_range_end(domains, domain))
509 def validate_route(self, network, domain, route):
510 if (not self.is_dict(route) or
511 self.TO not in route or
512 self.VIA not in route or
513 not self.val_is_str(route, self.TO) or
514 not self.val_is_str(route, self.VIA)):
515 self.err_invalid_routes(network, domain)
516 self.utils.validate_subnet_address(route[self.TO])
517 self.utils.validate_ip_address(route[self.VIA])
518 if route[self.TO] == self.DEFAULT_ROUTE_DEST:
519 self.err_default_route(network, domain)
521 def validate_providernet_name(self, netname):
522 if not isinstance(netname, basestring) or not re.match(self.PROVNET_NAME_MATCH, netname):
523 self.err_invalid_provnet_name()
524 if len(netname) > self.MAX_PROVNET_LEN:
525 self.err_provnet_len()
527 def validate_vlan_ranges(self, provnet_conf, provnet):
528 self.must_be_str(provnet_conf, provnet, self.VLAN_RANGES)
530 for vlan_range in provnet_conf[provnet][self.VLAN_RANGES].split(','):
531 vids = vlan_range.split(':')
533 self.err_vlan_ranges_format(provnet)
538 self.err_vlan_ranges_format(provnet)
539 self.validate_vlan_id(provnet, start)
540 self.validate_vlan_id(provnet, end)
542 self.err_vlan_ranges_format(provnet)
543 vlan_ranges.append([start, end])
544 self.validate_vlan_ranges_not_overlapping(vlan_ranges)
546 def validate_vlan_ranges_not_overlapping(self, vlan_ranges):
547 for idx, range1 in enumerate(vlan_ranges):
548 for range2 in vlan_ranges[(idx+1):]:
549 if not (range1[0] > range2[1] or range1[1] < range2[0]):
550 self.err_vlan_ranges_overlapping(range1, range2)
552 def validate_vlan_id(self, network, vid):
553 if vid < self.MIN_VLAN or vid > self.MAX_VLAN:
554 self.err_vlan(network)