blob: 856038a050b3b5a4db762aaea2f819e5ef8afaba [file] [log] [blame]
srikanth116e6e82014-08-19 07:22:37 -07001#
2# Copyright (c) 2013 Big Switch Networks, Inc.
3#
4# Licensed under the Eclipse Public License, Version 1.0 (the
5# "License"); you may not use this file except in compliance with the
6# License. You may obtain a copy of the License at
7#
8# http://www.eclipse.org/legal/epl-v10.html
9#
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
13# implied. See the License for the specific language governing
14# permissions and limitations under the License.
15#
16
17import re
18import os.path
19
20from django.forms import ValidationError
21from django.core.validators import RegexValidator, MinValueValidator, MaxValueValidator
22
23validate_mac_address = RegexValidator('^(([A-Fa-f\d]){2}:?){5}[A-Fa-f\d]{2}$', 'MAC address is a 48-bit quantity, expressed in hex as AA:BB:CC:DD:EE:FF', 'invalid')
24validate_dpid = RegexValidator('^([A-Fa-f\d]{2}:?){7}[A-Fa-f\d]{2}$', 'Switch DPID is a 64-bit quantity, expressed in hex as AA:BB:CC:DD:EE:FF:00:11', 'invalid')
25validate_controller_id = RegexValidator('^[A-Fa-f\d]{8}-([A-Fa-f\d]{4}-){3}[A-Fa-f\d]{12}$', 'Controller ID is a 128-bit quantity, expressed in hex as aabbccdd-eeff-0011-2233-445566778899', 'invalid')
26
27class UfwProtocolValditor(object):
28 def __init__(self):
29 pass
30
31 def __call__(self, value):
32 if not (value != "tcp" or value != "udp" or value != 'vrrp'):
33 raise ValidationError("Protocol must be either 'tcp' or 'udp' or 'vrrp'")
34
35octet = r'(?:0|[1-9][0-9]*)'
36IP_RE = re.compile("^" + octet + '[.]' + octet
37 + '[.]' + octet + '[.]' + octet + "$")
38
39class IpValidator(object):
40 def __call__(self, value):
41 if not IP_RE.match(value) or len([x for x in value.split('.') if int(x) < 256]) != 4:
42 raise ValidationError("IP must be in dotted decimal format, 234.0.59.1")
43
44class IpMaskValidator(object):
45 def __call__(self, value):
46 if not IP_RE.match(value):
47 raise ValidationError("IP must be in dotted decimal format, 255.255.128.0")
48 invalid_netmask = False
49 invalid_inverse_netmask = False
50 lookForZero = False
51 for x in value.split('.'):
52 xInt = int(x)
53 if lookForZero:
54 if xInt:
55 invalid_netmask = True
56 break # go check inverse mask
57 if xInt != 255:
58 if xInt:
59 if xInt >= 128:
60 while (xInt % 2) == 0:
61 xInt = xInt >> 1
62 if xInt & (xInt + 1) == 0:
63 continue
64 invalid_netmask = True
65 break # go check inverse mask
66 lookForZero = True
67
68 lookForOne = False
69 for x in value.split('.'):
70 xInt = int(x)
71 if lookForOne: # all bits should be 1 bits
72 if (xInt != 255):
73 invalid_inverse_netmask = True
74 break
75 else:
76 continue
77 if xInt != 0:
78 # check for contiguous 1-bits from LSb
79 lookForOne = True
80 if xInt == 255:
81 continue
82 if xInt & (xInt + 1) != 0:
83 invalid_inverse_netmask = True
84 break
85 if invalid_netmask and invalid_inverse_netmask: # no valid mask, then raise exception
86 raise ValidationError("Invalid IP Mask, should be like 255.255.128.0 or 0.0.127.255")
87
88
89class PortRangeSpecValidator(object):
90 def __init__(self):
91 self.RANGE_RE = re.compile(r'^([A-Za-z0-9-/\.:]*?)(\d+)\-(\d+)$')
92 self.SINGLE_RE = re.compile(r'^([A-Za-z0-9-/\.:]+)$')
93 def __call__(self, value):
94 values = value.split(',')
95 for v in values:
96 m = self.RANGE_RE.match(v);
97 if (m):
98 startport = int(m.group(2))
99 endport = int(m.group(3))
100 if (startport >= endport):
101 raise ValidationError("Invalid range numerals: %d-%d" % (startport, endport))
102 elif not self.SINGLE_RE.match(v):
103 raise ValidationError("Must be a list of ports or ranges, such as 'A10-15,B25,C1-13'")
104
105class VLANRangeSpecValidator(object):
106 def __init__(self):
107 self.RANGE_RE = re.compile(r'^([\d]+)\-([\d]+)$')
108 self.SINGLE_RE = re.compile(r'^[\d]+$')
109
110 def __call__(self, value):
111 values = value.split(',')
112 for v in values:
113 m = self.RANGE_RE.match(v);
114 if v == 'untagged':
115 pass
116 elif (m):
117 if (int(m.group(1)) >= int(m.group(2))):
118 raise ValidationError("Invalid range numerals in {}: {} must be less than {}".format(v, m.group(1), m.group(2)))
119 elif (int(m.group(1)) > 4095):
120 raise ValidationError("Invalid VLAN: {} must be in range 0-4095".format(m.group(1)))
121 elif (int(m.group(2)) < 0):
122 raise ValidationError("Invalid VLAN: {} must be in range 0-4095".format(m.group(2)))
123 elif not self.SINGLE_RE.match(v):
124 raise ValidationError("Must be a list of VLANs or ranges,"
125 " such as '5-20,45,4053,untagged'")
126 elif int(v) > 4095 or int(v) < 0:
127 raise ValidationError("Invalid VLAN: {} must be in range 0-4095".format(v))
128
129class TagSpecValidator(object):
130 def __init__(self):
131 self.TAG_NAME_RE = re.compile(r'^([a-zA-Z0-9_-]+(?:\.?[a-zA-Z0-9_-]+)*)=+([a-zA-Z0-9_-]+)$')
132
133 def __call__(self,value):
134 values = value.split(',')
135 for v in values:
136 v = v.strip()
137 if not self.TAG_NAME_RE.match(v):
138 raise ValidationError("Invalid tag: " +
139 '='.join(v.split('|')) +
140 "\nFormat: tag namespace.namel value1, " +
141 "where namespace may be empty or must be a-z, A-Z, 0-9, -, . or _, " +
142 "name and value must be a-z, A-Z, 0-9, - or _")
143
144class CidrValidator(object):
145 def __init__(self, mask_required=True):
146 self.mask_required = mask_required
147 self.CIDR_RE = re.compile(r'^(\d{1,3}\.){3}\d{1,3}/\d{1,2}?$')
148
149 def __call__(self, value):
150 ip_validator = IpValidator()
151 if self.CIDR_RE.match(value):
152 ip, mask = value.split('/')
153 ip_validator(ip)
154 if int(mask) < 1 or int(mask) > 32:
155 raise ValidationError("Mask should be between 1-32")
156 else:
157 if self.mask_required: # if mask was required and we didn't match above, problem!
158 raise ValidationError("Must be in dotted decimal format with a mask between 1-32")
159 else:
160 ip_validator(value)
161
162class EnumerationValidator(object):
163 def __init__(self, enumerated_values, case_sensitive=False, message=None, code=None):
164 self.case_sensitive = case_sensitive
165 if case_sensitive:
166 self.enumerated_values = enumerated_values
167 else:
168 self.enumerated_values = [s.lower() for s in enumerated_values]
169 if not message:
170 message = 'Invalid enumerated value'
171 self.message = message
172 if not code:
173 code = 'invalid'
174 self.code = code
175
176 def __call__(self, value):
177 if not self.case_sensitive:
178 value = value.lower()
179 if value not in self.enumerated_values:
180 raise ValidationError(self.message, self.code)
181
182class ChoicesValidator(EnumerationValidator):
183 def __init__(self, choices, case_sensitive=False, message=None, code=None):
184 enumerated_values = [choice[0] for choice in choices]
185 EnumerationValidator.__init__(self, enumerated_values, case_sensitive, message, code)
186
187class RangeValidator(object):
188 def __init__(self, min, max):
189 self.min_value_validator = MinValueValidator(min)
190 self.max_value_validator = MaxValueValidator(max)
191
192 def __call__(self, value):
193 try:
194 self.min_value_validator(int(value))
195 self.max_value_validator(int(value))
196 except ValidationError:
197 raise ValidationError('Ensure this value is within the range (%d-%d)' %
198 (self.min_value_validator.limit_value,
199 self.max_value_validator.limit_value))
200
201class ControllerAliaVsalidator(object):
202 def __init__(self):
203 self.CONTROLLER_ALIAS_RE = re.compile(r'^[a-zA-Z0-9_-]+$')
204
205 def __call__(self, value):
206 if not self.CONTROLLER_ALIAS_RE.match(value):
207 raise ValidationError("controller alias name must ba a-z, 0-9, _ or _")
208
209class SwitchAliasValidator(object):
210 def __init__(self):
211 self.SWITCH_ALIAS_RE = re.compile(r'^[a-zA-Z0-9_-]+$')
212
213 def __call__(self, value):
214 if not self.SWITCH_ALIAS_RE.match(value):
215 raise ValidationError("switch alias name must ba a-z, 0-9, _ or _")
216
217class PortAliasValidator(object):
218 def __init__(self):
219 self.PORT_ALIAS_RE = re.compile(r'^[a-zA-Z0-9_-]+$')
220
221 def __call__(self, value):
222 if not self.PORT_ALIAS_RE.match(value):
223 raise ValidationError("port alias name must ba a-z, 0-9, _ or _")
224
225class HostAliasValidator(object):
226 def __init__(self):
227 self.HOST_ALIAS_RE = re.compile(r'^[a-zA-Z0-9_-]+$')
228
229 def __call__(self, value):
230 if not self.HOST_ALIAS_RE.match(value):
231 raise ValidationError("host alias name must ba a-z, 0-9, _ or _")
232
233class AddressSpaceNameValidator(object):
234 def __init__(self):
235 self.ADDRESS_SPACE_NAME_RE = re.compile(r'^[a-zA-Z0-9_-]+$')
236
237 def __call__(self,value):
238 if not self.ADDRESS_SPACE_NAME_RE.match(value):
239 raise ValidationError("address-space name must be a-z, A-Z, 0-9, - or _")
240
241class TenantNameValidator(object):
242 def __init__(self):
243 self.TENANT_NAME_RE = re.compile(r'^[a-zA-Z0-9_-]+$')
244
245 def __call__(self,value):
246 if not self.TENANT_NAME_RE.match(value):
247 raise ValidationError("tenant name must be a-z, A-Z, 0-9, - or _")
248
249class GeneralNameValidator(object):
250 def __init__(self):
251 self.GENERAL_NAME_RE = re.compile(r'^[a-zA-Z0-9_-]+$')
252
253 def __call__(self,value):
254 if not self.GENERAL_NAME_RE.match(value):
255 raise ValidationError("Name must be a-z, A-Z, 0-9, - or _")
256
257class VnsNameValidator(object):
258 def __init__(self):
259 self.VNS_NAME_RE = re.compile(r'^[a-zA-Z0-9_-]+$')
260
261 def __call__(self,value):
262 if not self.VNS_NAME_RE.match(value):
263 raise ValidationError("vns name must be a-z, A-Z, 0-9, - or _")
264
265class VnsInterfaceNameValidator(object):
266 def __init__(self):
267 self.VNS_INTERFACE_RE = re.compile(r'^[a-zA-Z0-9_-]+$')
268 self.PORT_RE = re.compile(r'^([A-Za-z0-9-]*?)(\d+)$')
269 self.MAC_RE = re.compile(r'^(([A-Fa-f\d]){2}:?){5}[A-Fa-f\d]{2}$')
270
271 def __call__(self,value):
272 if not self.VNS_INTERFACE_RE.match(value):
273 items = value.split('/')
274 if len(items) == 2:
275 if not self.PORT_RE.match(items[1]) and \
276 not self.MAC_RE.match(items[1]):
277 raise ValidationError("interface name after '/' must be either a port or a mac address")
278 else:
279 raise ValidationError("invalid syntax for interface name")
280
281class VnsAclNameValidator(object):
282 def __init__(self):
283 self.VNS_ACL_NAME_RE = re.compile(r'[a-zA-Z0-9_-]+$')
284
285 def __call__(self,value):
286 if not self.VNS_ACL_NAME_RE.match(value):
287 raise ValidationError("acl name must be a-z, A-Z, 0-9, - or _")
288
289class VnsAclEntryActionValidator(object):
290 def __call__(self,value):
291 if not "permit".startswith(value.lower()) and \
292 not "deny".startswith(value.lower()):
293 raise ValidationError("acl entry action must be 'permit' or 'deny'")
294
295class VnsInterfaceAclInOutValidator(object):
296 def __call__(self,value):
297 if not "in".startswith(value.lower()) and \
298 not "out".startswith(value.lower()):
299 raise ValidationError("acl entry action must be 'permit' or 'deny'")
300
301class VnsRuleNameValidator(object):
302 def __init__(self):
303 self.IF_NAME_RE = re.compile(r'^\d*$')
304
305 def __call__(self, value):
306 if not self.IF_NAME_RE.match(value):
307 print value
308 raise ValidationError("Invalid rule name, only digits allowed")
309
310class TagNameValidator(object):
311 def __init__(self):
312 self.TAG_NAME_RE = re.compile(r'^[a-zA-Z0-9_-]+(?:\.?[a-zA-Z0-9_-]+)*(?:\|[a-zA-Z0-9_-]+)+$')
313
314 def __call__(self,value):
315 if not self.TAG_NAME_RE.match(value):
316 raise ValidationError("Invalid tag: " + value + "\nFormat: tag namespace|namel|value1, " +
317 "where namespace may be empty or must be a-z, A-Z, 0-9, -, . or _, " +
318 "name and value must be a-z, A-Z, 0-9, - or _")
319
320
321class VnsArpModeValidator(object):
322 def __init__(self):
323 self.ARP_MODES = ['flood-if-unknown', 'always-flood', 'drop-if-unknown']
324
325 def __call__(self,value):
326 if not value in self.ARP_MODES:
327 raise ValidationError("must be one of %s" % ', '.join(self.ARP_MODES))
328
329class VnsDhcpModeValidator(object):
330 def __init__(self):
331 self.DHCP_MODES = ['flood-if-unknown', 'always-flood', 'static']
332
333 def __call__(self,value):
334 if not value in self.DHCP_MODES:
335 raise ValidationError("must be one of %s" % ', '.join(self.DHCP_MODES))
336
337class VnsBroadcastModeValidator(object):
338 def __init__(self):
339 self.BROADCAST_MODES = ['drop', 'always-flood', 'forward-to-known']
340
341 def __call__(self,value):
342 if not value in self.BROADCAST_MODES:
343 raise ValidationError("must be one of %s" % ', '.join(self.BROADCAST_MODES))
344
345class IntfNameValidator(object):
346 def __init__(self):
347 self.IntfName_RE = re.compile(r'^ethernet[0-9]')
348
349 def __call__(self, value):
350 if not self.IntfName_RE.match(value):
351 raise ValidationError("Interface name must be Ethernet<num>")
352
353class DomainNameValidator(object):
354 def __init__(self):
355 self.DomainName_RE = re.compile(r'^([a-zA-Z0-9-]+.?)+$')
356 def __call__(self,value):
357 if not self.DomainName_RE.match(value):
358 raise ValidationError("Value must be a valid domain name")
359
360class IpOrDomainNameValidator(object):
361 def __init__(self):
362 self.DomainName_RE = re.compile(r'^([a-zA-Z0-9-]+.?)+$')
363
364 def __call__(self,value):
365 if not IP_RE.match(value) and not self.DomainName_RE.match(value):
366 raise ValidationError("Value must be a valid IP address or domain name")
367
368class TimeZoneValidator(object):
369 timezones = None
370 def __call__(self, value):
371 if not TimeZoneValidator.timezones:
372 import pytz
373 TimeZoneValidator.timezones = pytz.all_timezones
374 if value not in TimeZoneValidator.timezones:
375 raise ValidationError("Invalid time zone string")
376
377class VCenterMgrIdsValidator(object):
378 def __init__(self):
379 self.VCenterMgrId_RE = re.compile(r'^[a-zA-Z]([a-zA-Z0-9_-])*')
380 def __call__(self,value):
381 if not self.VCenterMgrId_RE.match(value):
382 raise ValidationError("Value must be a valid name, starts with a alphabet and can have alphabets, numbers, _ and -")
383
384class VCenterObjNamesValidator(object):
385 def __init__(self):
386 self.VCenterObjName_RE = re.compile(r'(?!.*\|.*)')
387 def __call__(self, value):
388 if not self.VCenterObjName_RE.match(value):
389 raise ValidationError("VCenter object names can contain any character except '|'")
390
391class FeatureValidator(object):
392 """Validates that a specific feature has been installed
393
394 We assume that a feature has been installed if the following file exists:
395 {sdncon.SDN_ROOT}/feature/<feature-name>
396
397 NOTE: The feature is enabled/disabled for use by updating the controller
398 object, this validator just ensures that before we enable a feature, it
399 has been actually installed.
400 """
401 def __init__(self, feature, featuredir=None):
402 self.feature = feature
403 if featuredir is None:
404 featuredir = \
405 os.path.join(os.path.sep, 'opt', 'sdnplatform', 'feature')
406 self.featurefile = os.path.join(featuredir, feature)
407
408 def __call__(self, value):
409 if value and not os.path.isfile(self.featurefile):
410 raise ValidationError(
411 'Feature not installed, please install "%s"' %
412 self.feature)
413
414class VlanStringValidator(object):
415 def __call__(self, value):
416 self.vlan = 0
417 try:
418 self.vlan = int(value)
419 if (self.vlan < 1 or self.vlan > 4095):
420 raise ValidationError("VLAN must be in the range of 1 to 4095 with 4095 as untagged")
421 except ValueError:
422 raise ValidationError(
423 "VLAN must be in the range of 1 to 4095 with 4095 as untagged")
424
425
426class SafeForPrimaryKeyValidator(object):
427 """Validates that a string does not contain any character that would be
428 illegal for use in a primary key. Currently this is the pipe | symbol
429 """
430
431 def __call__(self, value):
432 if "|" in value:
433 raise ValidationError(
434 "The pipe symbol '|' is not a valid character")
435
436
437class IsRegexValidator(object):
438 """Validates that a given string is a valid regular expression
439 """
440
441 def __call__(self, value):
442 try:
443 value = str(value)
444 re.compile(value)
445 except re.error as e:
446 raise ValidationError(
447 "Input is not a valid regular expression: %s", e)
448