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'
43 INFRA_NETWORKS = [INFRA_EXTERNAL,
45 INFRA_STORAGE_CLUSTER]
52 IP_START = 'ip_range_start'
53 IP_END = 'ip_range_end'
58 PROVIDER_NETWORKS = 'provider_networks'
59 VLAN_RANGES = 'vlan_ranges'
62 INPUT_ERR_CONTEXT = 'validate_set() input'
63 ERR_INPUT_NOT_DICT = 'Invalid %s, not a dictionary' % INPUT_ERR_CONTEXT
65 ERR_MISSING = 'Missing {1} configuration in {0}'
66 ERR_NOT_DICT = 'Invalid {1} value in {0}: Empty or not a dictionary'
67 ERR_NOT_LIST = 'Invalid {1} value in {0}: Empty, contains duplicates or not a list'
68 ERR_NOT_STR = 'Invalid {1} value in {0}: Not a string'
69 ERR_NOT_INT = 'Invalid {1} value in {0}: Not an integer'
70 ERR_NOT_BOOL = 'Invalid {1} value in {0}: Not a boolean value'
72 ERR_MTU = 'Invalid {} mtu: Not in range %i - %i' % (MIN_MTU, MAX_MTU)
73 ERR_VLAN = 'Invalid {} vlan: Not in range %i - %i' % (MIN_VLAN, MAX_VLAN)
74 ERR_DUPLICATE_INFRA_VLAN = 'Same VLAN ID {} used for multiple infra networks'
75 ERR_CIDRS_OVERLAPPING = 'Network CIDR values {} and {} are overlapping'
76 ERR_GW_NOT_SUPPORTED = 'Gateway address not supported for {}'
77 ERR_INVALID_ROUTES = 'Invalid static routes format for {0} {1}'
78 ERR_DEFAULT_ROUTE = 'Default route not supported for {0} {1}'
80 ERR_VLAN_RANGES_FORMAT = 'Invalid {} vlan_ranges format'
81 ERR_VLAN_RANGES_OVERLAPPING = 'Provider network vlan ranges {} and {} are overlapping'
83 ERR_INVALID_PROVNET_NAME = 'Invalid provider network name'
84 ERR_PROVNET_LEN = 'Too long provider network name, max %s chars' % MAX_PROVNET_LEN
85 ERR_SHARED_NETWORKS = 'Only one provider network can be configured as shared'
87 ERR_INVALID_NET_DOMAIN_NAME = 'Invalid network domain name'
88 ERR_NET_DOMAIN_LEN = 'Too long network domain name, max %s chars' % MAX_NET_DOMAIN_LEN
90 ERR_TOO_MANY_DNS = 'Too many DNS server IP addresses, max %i supported' % MAX_DNS
92 ERR_MTU_INSIDE_NETWORK_DOMAIN = 'Missplaced MTU inside {} network domain {}'
95 def err_input_not_dict():
96 raise validation.ValidationError(NetworkingValidation.ERR_INPUT_NOT_DICT)
99 def err_missing(context, key):
100 raise validation.ValidationError(NetworkingValidation.ERR_MISSING.format(context, key))
103 def err_not_dict(context, key):
104 raise validation.ValidationError(NetworkingValidation.ERR_NOT_DICT.format(context, key))
107 def err_not_list(context, key):
108 raise validation.ValidationError(NetworkingValidation.ERR_NOT_LIST.format(context, key))
111 def err_not_str(context, key):
112 raise validation.ValidationError(NetworkingValidation.ERR_NOT_STR.format(context, key))
115 def err_not_int(context, key):
116 raise validation.ValidationError(NetworkingValidation.ERR_NOT_INT.format(context, key))
119 def err_not_bool(context, key):
120 raise validation.ValidationError(NetworkingValidation.ERR_NOT_BOOL.format(context, key))
123 def err_mtu(context):
124 raise validation.ValidationError(NetworkingValidation.ERR_MTU.format(context))
127 def err_vlan(context):
128 raise validation.ValidationError(NetworkingValidation.ERR_VLAN.format(context))
131 def err_duplicate_vlan(vid):
132 raise validation.ValidationError(NetworkingValidation.ERR_DUPLICATE_INFRA_VLAN.format(vid))
135 def err_vlan_ranges_format(provnet):
136 err = NetworkingValidation.ERR_VLAN_RANGES_FORMAT.format(provnet)
137 raise validation.ValidationError(err)
140 def err_vlan_ranges_overlapping(range1, range2):
141 ranges = sorted([range1, range2])
142 err = NetworkingValidation.ERR_VLAN_RANGES_OVERLAPPING.format(ranges[0], ranges[1])
143 raise validation.ValidationError(err)
146 def err_invalid_provnet_name():
147 raise validation.ValidationError(NetworkingValidation.ERR_INVALID_PROVNET_NAME)
150 def err_provnet_len():
151 raise validation.ValidationError(NetworkingValidation.ERR_PROVNET_LEN)
154 def err_invalid_net_domain_name():
155 raise validation.ValidationError(NetworkingValidation.ERR_INVALID_NET_DOMAIN_NAME)
158 def err_net_domain_len():
159 raise validation.ValidationError(NetworkingValidation.ERR_NET_DOMAIN_LEN)
162 def err_cidrs_overlapping(cidr1, cidr2):
163 cidrs = sorted([cidr1, cidr2])
164 err = NetworkingValidation.ERR_CIDRS_OVERLAPPING.format(cidrs[0], cidrs[1])
165 raise validation.ValidationError(err)
168 def err_gw_not_supported(network):
169 raise validation.ValidationError(NetworkingValidation.ERR_GW_NOT_SUPPORTED.format(network))
172 def err_invalid_routes(network, domain):
173 err = NetworkingValidation.ERR_INVALID_ROUTES.format(network, domain)
174 raise validation.ValidationError(err)
177 def err_default_route(network, domain):
178 err = NetworkingValidation.ERR_DEFAULT_ROUTE.format(network, domain)
179 raise validation.ValidationError(err)
182 def err_too_many_dns():
183 raise validation.ValidationError(NetworkingValidation.ERR_TOO_MANY_DNS)
186 def err_shared_networks():
187 raise validation.ValidationError(NetworkingValidation.ERR_SHARED_NETWORKS)
190 def err_mtu_inside_network_domain(infra, domain):
191 err = NetworkingValidation.ERR_MTU_INSIDE_NETWORK_DOMAIN.format(infra, domain)
192 raise validation.ValidationError(err)
196 return isinstance(conf, dict)
199 def key_exists(conf_dict, key):
200 return key in conf_dict
203 def val_is_str(conf_dict, key):
204 return isinstance(conf_dict[key], basestring)
207 def val_is_list(conf_dict, key):
208 return isinstance(conf_dict[key], list)
211 def val_is_non_empty_list(conf_dict, key):
212 return (isinstance(conf_dict[key], list) and
213 len(conf_dict[key]) > 0 and
214 len(conf_dict[key]) == len(set(conf_dict[key])))
217 def val_is_non_empty_dict(conf_dict, key):
218 return isinstance(conf_dict[key], dict) and len(conf_dict[key]) > 0
221 def val_is_int(conf_dict, key):
222 return isinstance(conf_dict[key], (int, long))
225 def val_is_bool(conf_dict, key):
226 return isinstance(conf_dict[key], bool)
229 def key_must_exist(conf_dict, entry, key):
230 if not NetworkingValidation.key_exists(conf_dict[entry], key):
231 NetworkingValidation.err_missing(entry, key)
234 def must_be_str(conf_dict, entry, key):
235 NetworkingValidation.key_must_exist(conf_dict, entry, key)
236 if not NetworkingValidation.val_is_str(conf_dict[entry], key):
237 NetworkingValidation.err_not_str(entry, key)
240 def must_be_list(conf_dict, entry, key):
241 NetworkingValidation.key_must_exist(conf_dict, entry, key)
242 if not NetworkingValidation.val_is_non_empty_list(conf_dict[entry], key):
243 NetworkingValidation.err_not_list(entry, key)
246 def must_be_dict(conf_dict, entry, key):
247 NetworkingValidation.key_must_exist(conf_dict, entry, key)
248 if not NetworkingValidation.val_is_non_empty_dict(conf_dict[entry], key):
249 NetworkingValidation.err_not_dict(entry, key)
252 def exists_as_dict(conf_dict, entry, key):
253 if not NetworkingValidation.key_exists(conf_dict[entry], key):
255 if not NetworkingValidation.val_is_non_empty_dict(conf_dict[entry], key):
256 NetworkingValidation.err_not_dict(entry, key)
260 def exists_as_int(conf_dict, entry, key):
261 if not NetworkingValidation.key_exists(conf_dict[entry], key):
263 if not NetworkingValidation.val_is_int(conf_dict[entry], key):
264 NetworkingValidation.err_not_int(entry, key)
268 def exists_as_bool(conf_dict, entry, key):
269 if not NetworkingValidation.key_exists(conf_dict[entry], key):
271 if not NetworkingValidation.val_is_bool(conf_dict[entry], key):
272 NetworkingValidation.err_not_bool(entry, key)
276 cmvalidator.CMValidator.__init__(self)
277 self.utils = validation.ValidationUtils()
281 def get_subscription_info(self):
282 return self.SUBSCRIPTION
284 def validate_set(self, props):
285 self.prepare_validate(props)
288 def prepare_validate(self, props):
289 if not self.is_dict(props):
290 self.err_input_not_dict()
292 if not self.key_exists(props, self.DOMAIN):
293 self.err_missing(self.INPUT_ERR_CONTEXT, self.DOMAIN)
295 self.net_conf = json.loads(props[self.DOMAIN])
296 self.conf = {self.DOMAIN: self.net_conf}
298 if not self.val_is_non_empty_dict(self.conf, self.DOMAIN):
299 self.err_not_dict(self.INPUT_ERR_CONTEXT, self.DOMAIN)
303 self.validate_default_mtu()
304 self.validate_infra_networks()
305 self.validate_provider_networks()
306 self.validate_no_overlapping_cidrs()
308 def validate_dns(self):
309 self.must_be_list(self.conf, self.DOMAIN, self.DNS)
310 for server in self.net_conf[self.DNS]:
311 self.utils.validate_ip_address(server)
312 if len(self.net_conf[self.DNS]) > self.MAX_DNS:
313 self.err_too_many_dns()
315 def validate_default_mtu(self):
316 self.validate_mtu(self.conf, self.DOMAIN)
318 def validate_infra_networks(self):
319 self.validate_infra_internal()
320 self.validate_infra_external()
321 self.validate_infra_storage_cluster()
322 self.validate_no_duplicate_infra_vlans()
324 def validate_infra_internal(self):
325 self.validate_network_exists(self.INFRA_INTERNAL)
326 self.validate_infra_network(self.INFRA_INTERNAL)
327 self.validate_no_gateway(self.INFRA_INTERNAL)
329 def validate_infra_external(self):
330 self.validate_network_exists(self.INFRA_EXTERNAL)
331 self.validate_infra_network(self.INFRA_EXTERNAL)
332 self.validate_gateway(self.INFRA_EXTERNAL)
334 def validate_infra_storage_cluster(self):
335 if self.network_exists(self.INFRA_STORAGE_CLUSTER):
336 self.validate_network_domains(self.INFRA_STORAGE_CLUSTER)
337 self.validate_infra_network(self.INFRA_STORAGE_CLUSTER)
338 self.validate_no_gateway(self.INFRA_STORAGE_CLUSTER)
340 def validate_infra_network(self, network, vlan_must_exist=False):
341 self.validate_mtu(self.net_conf, network)
342 self.validate_cidr(network)
343 self.validate_vlan(network, vlan_must_exist)
344 self.validate_ip_range(network)
345 self.validate_routes(network)
346 self.validate_no_mtu_inside_network_domain(network)
348 def validate_no_duplicate_infra_vlans(self):
350 for network in self.INFRA_NETWORKS:
351 if self.key_exists(self.net_conf, network):
352 for domain, domain_conf in self.net_conf[network][self.NETWORK_DOMAINS].iteritems():
353 if self.key_exists(domain_conf, self.VLAN):
354 if domain not in domvids:
356 domvids[domain].append(domain_conf[self.VLAN])
357 for vids in domvids.itervalues():
359 for vid in sorted(vids):
361 self.err_duplicate_vlan(vid)
364 def validate_no_overlapping_cidrs(self):
366 for network in self.INFRA_NETWORKS:
367 if self.key_exists(self.net_conf, network):
368 for domain_conf in self.net_conf[network][self.NETWORK_DOMAINS].itervalues():
369 cidrs.append(IPNetwork(domain_conf[self.CIDR]))
370 for idx, cidr1 in enumerate(cidrs):
371 for cidr2 in cidrs[(idx+1):]:
372 if not (cidr1[0] > cidr2[-1] or cidr1[-1] < cidr2[0]):
373 self.err_cidrs_overlapping(str(cidr1), str(cidr2))
375 def validate_ip_range(self, network):
376 domains = self.net_conf[network][self.NETWORK_DOMAINS]
377 for domain in domains:
378 ip_start = self.get_ip_range_start(domains, domain)
379 ip_end = self.get_ip_range_end(domains, domain)
380 self.utils.validate_ip_range(ip_start, ip_end)
382 def get_ip_range_start(self, domains, domain):
383 if self.key_exists(domains[domain], self.IP_START):
384 self.validate_ip_range_limiter(domains, domain, self.IP_START)
385 return domains[domain][self.IP_START]
386 return str(IPNetwork(domains[domain][self.CIDR])[1])
388 def get_ip_range_end(self, domains, domain):
389 if self.key_exists(domains[domain], self.IP_END):
390 self.validate_ip_range_limiter(domains, domain, self.IP_END)
391 return domains[domain][self.IP_END]
392 return str(IPNetwork(domains[domain][self.CIDR])[-2])
394 def validate_ip_range_limiter(self, domains, domain, key):
395 self.must_be_str(domains, domain, key)
396 self.utils.validate_ip_address(domains[domain][key])
397 self.utils.validate_ip_in_subnet(domains[domain][key],
398 domains[domain][self.CIDR])
400 def validate_provider_networks(self):
401 if self.network_exists(self.PROVIDER_NETWORKS):
402 for netname in self.net_conf[self.PROVIDER_NETWORKS]:
403 self.validate_providernet(netname)
404 self.validate_shared_provider_network(self.net_conf[self.PROVIDER_NETWORKS])
406 def validate_providernet(self, netname):
407 self.validate_providernet_name(netname)
408 self.must_be_dict(self.net_conf, self.PROVIDER_NETWORKS, netname)
409 self.validate_mtu(self.net_conf[self.PROVIDER_NETWORKS], netname)
410 self.validate_vlan_ranges(self.net_conf[self.PROVIDER_NETWORKS], netname)
412 def validate_shared_provider_network(self, provider_conf):
414 for netname in provider_conf:
415 if self.exists_as_bool(provider_conf, netname, self.SHARED):
416 if provider_conf[netname][self.SHARED] is True:
418 if shared_counter > 1:
419 self.err_shared_networks()
421 def validate_mtu(self, conf, network):
422 if self.exists_as_int(conf, network, self.MTU):
423 mtu = conf[network][self.MTU]
424 if mtu < self.MIN_MTU or mtu > self.MAX_MTU:
425 self.err_mtu(network)
427 def validate_no_mtu_inside_network_domain(self, network):
428 domains = self.net_conf[network][self.NETWORK_DOMAINS]
429 for domain in domains:
430 if self.key_exists(domains[domain], self.MTU):
431 self.err_mtu_inside_network_domain(network, domain)
433 def validate_vlan(self, network, must_exist=False):
434 domains = self.net_conf[network][self.NETWORK_DOMAINS]
435 for domain in domains:
436 if must_exist and not self.key_exists(domains[domain], self.VLAN):
437 self.err_missing(network, self.VLAN)
438 if self.exists_as_int(domains, domain, self.VLAN):
439 self.validate_vlan_id(network, domains[domain][self.VLAN])
441 def validate_network_exists(self, network):
442 self.must_be_dict(self.conf, self.DOMAIN, network)
443 self.validate_network_domains(network)
445 def validate_network_domains(self, network):
446 self.must_be_dict(self.net_conf, network, self.NETWORK_DOMAINS)
447 for domain in self.net_conf[network][self.NETWORK_DOMAINS]:
448 self.validate_net_domain_name(domain)
450 def validate_net_domain_name(self, domain_name):
451 if (not isinstance(domain_name, basestring) or
452 not re.match(self.NET_DOMAIN_MATCH, domain_name)):
453 self.err_invalid_net_domain_name()
454 if len(domain_name) > self.MAX_NET_DOMAIN_LEN:
455 self.err_net_domain_len()
457 def network_exists(self, network):
458 return self.exists_as_dict(self.conf, self.DOMAIN, network)
460 def validate_cidr(self, network):
461 domains = self.net_conf[network][self.NETWORK_DOMAINS]
462 for domain in domains:
463 self.must_be_str(domains, domain, self.CIDR)
464 self.utils.validate_subnet_address(domains[domain][self.CIDR])
466 def validate_gateway(self, network):
467 domains = self.net_conf[network][self.NETWORK_DOMAINS]
468 for domain in domains:
469 self.must_be_str(domains, domain, self.GATEWAY)
470 self.utils.validate_ip_address(domains[domain][self.GATEWAY])
471 self.utils.validate_ip_in_subnet(domains[domain][self.GATEWAY],
472 domains[domain][self.CIDR])
473 self.utils.validate_ip_not_in_range(domains[domain][self.GATEWAY],
474 self.get_ip_range_start(domains, domain),
475 self.get_ip_range_end(domains, domain))
477 def validate_no_gateway(self, network):
478 for domain_conf in self.net_conf[network][self.NETWORK_DOMAINS].itervalues():
479 if self.key_exists(domain_conf, self.GATEWAY):
480 self.err_gw_not_supported(network)
482 def validate_routes(self, network):
483 domains = self.net_conf[network][self.NETWORK_DOMAINS]
484 for domain in domains:
485 if self.key_exists(domains[domain], self.ROUTES):
486 if (not self.val_is_list(domains[domain], self.ROUTES) or
487 not domains[domain][self.ROUTES]):
488 self.err_invalid_routes(network, domain)
489 for route in domains[domain][self.ROUTES]:
490 self.validate_route(network, domain, route)
491 self.utils.validate_ip_in_subnet(route[self.VIA],
492 domains[domain][self.CIDR])
493 self.utils.validate_ip_not_in_range(route[self.VIA],
494 self.get_ip_range_start(domains, domain),
495 self.get_ip_range_end(domains, domain))
497 def validate_route(self, network, domain, route):
498 if (not self.is_dict(route) or
499 self.TO not in route or
500 self.VIA not in route or
501 not self.val_is_str(route, self.TO) or
502 not self.val_is_str(route, self.VIA)):
503 self.err_invalid_routes(network, domain)
504 self.utils.validate_subnet_address(route[self.TO])
505 self.utils.validate_ip_address(route[self.VIA])
506 if route[self.TO] == self.DEFAULT_ROUTE_DEST:
507 self.err_default_route(network, domain)
509 def validate_providernet_name(self, netname):
510 if not isinstance(netname, basestring) or not re.match(self.PROVNET_NAME_MATCH, netname):
511 self.err_invalid_provnet_name()
512 if len(netname) > self.MAX_PROVNET_LEN:
513 self.err_provnet_len()
515 def validate_vlan_ranges(self, provnet_conf, provnet):
516 self.must_be_str(provnet_conf, provnet, self.VLAN_RANGES)
518 for vlan_range in provnet_conf[provnet][self.VLAN_RANGES].split(','):
519 vids = vlan_range.split(':')
521 self.err_vlan_ranges_format(provnet)
526 self.err_vlan_ranges_format(provnet)
527 self.validate_vlan_id(provnet, start)
528 self.validate_vlan_id(provnet, end)
530 self.err_vlan_ranges_format(provnet)
531 vlan_ranges.append([start, end])
532 self.validate_vlan_ranges_not_overlapping(vlan_ranges)
534 def validate_vlan_ranges_not_overlapping(self, vlan_ranges):
535 for idx, range1 in enumerate(vlan_ranges):
536 for range2 in vlan_ranges[(idx+1):]:
537 if not (range1[0] > range2[1] or range1[1] < range2[0]):
538 self.err_vlan_ranges_overlapping(range1, range2)
540 def validate_vlan_id(self, network, vid):
541 if vid < self.MIN_VLAN or vid > self.MAX_VLAN:
542 self.err_vlan(network)