Loading cloudcontrol/common/allocation/__init__.py 0 → 100644 +14 −0 Original line number Diff line number Diff line # This file is part of CloudControl. # # CloudControl is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # CloudControl is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with CloudControl. If not, see <http://www.gnu.org/licenses/>. No newline at end of file cloudcontrol/common/allocation/vmspec.py 0 → 100644 +180 −0 Original line number Diff line number Diff line # This file is part of CloudControl. # # CloudControl is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # CloudControl is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with CloudControl. If not, see <http://www.gnu.org/licenses/>. import re class VMSpecValidationError(Exception): """ Exception raised when a VMSpec validation error occurs. """ # Checks: def check_type(object_, type_, message, **kwargs): if not isinstance(object_, type_): kwargs['type'] = type_.__class__.__name__ kwargs['object'] = str(object_) raise VMSpecValidationError(message % kwargs) def check_regex(object_, regex, message, **kwargs): if re.match(object_, regex): kwargs['object'] = str(object_) raise VMSpecValidationError(message % kwargs) def check_integer(object_, message, min=None, max=None, **kwargs): if not isinstance(object_, int): raise VMSpecValidationError(message % kwargs) elif (min is not None and object_ < min) or (max is not None and object_ > max): raise VMSpecValidationError(message % kwargs) def check_dict_key(dict_, key, message, **kwargs): if key not in dict_: kwargs['key'] = key raise VMSpecValidationError(message % kwargs) # Validation rules: def validate_riskgroup(data): """ Validate riskgroups. Example: {'name': {'tag': 1}} """ check_type(data, dict, 'riskgroup: must be a dict') for name, tags in data.iteritems(): env = {"name": name} check_type(name, basestring, 'riskgroup/[%(name)s]: must be a string', **env) check_type(tags, dict, 'riskgroup/%(name)s: must be a dict', **env) for tag, value in tags.iteritems(): check_type(tag, basestring, 'riskgroup/%(name)s/[%(tag)s]: must be a string', tag=tag, **env) check_integer(value, 'riskgroup/%(name)s/%(tag)s: must be a positive integer', min=1, tag=tag, **env) def validate_machine_volumes(hostname, data): """ Validate machines volumes. Example: {'root': {'size': 512000, 'pool': 'vg'}} """ check_type(data, list, 'machines/%(hostname)s/volumes: must be a list', hostname=hostname) for i, disk in enumerate(data): env = {'hostname': hostname, 'index': i} check_type(disk, dict, 'machines/%(hostname)s/volumes/%(index)s: must be a %(type)s', **env) check_dict_key(disk, 'size', 'machines/%(hostname)s/volumes/%(index)s/[%(key)s]: is mandatory', **env) check_integer(disk['size'], 'machines/%(hostname)ss/volumes/%(index)s/size: must be a positive integer', min=0, **env) check_dict_key(disk, 'pool', 'machines/%(hostname)s/volumes/%(index)s/[%(key)s]: is mandatory', **env) check_type(disk['pool'], basestring, 'machines/%(hostname)ss/volumes/%(index)s/pool: must be a string', **env) def validate_machine_spec(hostname, spec): """ Validate machines specifications. Example: {'cpu': 8, 'memory': 512000, 'flags': ['does_not_autostart'], 'tags': {'platform': 'Infra'}} """ env = {'hostname': hostname} check_integer(spec.get('cpu'), 'machines/%(hostname)s/cpu: must be a positive integer', min=1, **env) check_integer(spec.get('memory'), 'machines/%(hostname)s/memory: must be a positive integer', min=1, **env) if 'flags' in spec: if spec['flags'] is None: spec['flags'] = [] check_type(spec['flags'], list, 'machines/%(hostname)s/flags: must be a list', **env) for i, flag in enumerate(spec['flags']): check_type(flag, basestring, 'machines/%(hostname)s/flags[%(i)s]: must be a string', i=i, **env) if 'tags' in spec: if spec['tags'] is None: spec['tags'] = {} check_type(spec['tags'], dict, 'machines/%(hostname)s/tags: must be a dict', **env) for tag, value in spec['tags'].iteritems(): check_type(tag, basestring, 'machines/%(hostname)s/tags/[%(tag)s]: must be a string', tag=tag, **env) check_type(value, basestring, 'machines/%(hostname)s/tags/%(tag)s: must be a string', tag=tag, **env) if 'volumes' in spec: validate_machine_volumes(hostname, spec['volumes']) def validate_machines(data): """ Validate machine entry. Example: {'foobar-1.example.org': ...} """ check_type(data, dict, 'machines: must be a dict') if len(data) < 1: raise VMSpecValidationError('machines: at least one machine must be defined') for hostname, spec in data.iteritems(): if not re.match('^[a-z0-9-]+(.[a-z0-9-]+)*$', hostname): raise VMSpecValidationError('machines/[%s]: bad hostname format' % hostname) validate_machine_spec(hostname, spec) def validate_vmspec(data): """ Validate a vmspec. Example: {'riskgroups:' ..., 'machines': ...} """ if 'riskgroups' in data: validate_riskgroup(data['riskgroups']) check_dict_key(data, 'machines', 'machines: is mandatory') validate_machines(data['machines']) if 'target' in data: check_type(data['target'], basestring, 'target: must be a string (a TQL)') def expand_vmspec(vmspec): """ Expand the vmspec to a list of VM. """ validate_vmspec(vmspec) vmspec_expanded = [] riskgroups = vmspec.get('riskgroups', {}) for title, specs in vmspec['machines'].iteritems(): specs['title'] = title riskgroup = specs.get('tags', {}).get('riskgroup') if riskgroup is not None: if riskgroup not in riskgroups: raise VMSpecValidationError('machines/%s: riskgroup not defined') specs['riskgroup'] = riskgroups[riskgroup] vmspec_expanded.append(specs) return vmspec_expanded if __name__ == '__main__': import sys, json validate_vmspec(json.load(sys.stdin)) Loading
cloudcontrol/common/allocation/__init__.py 0 → 100644 +14 −0 Original line number Diff line number Diff line # This file is part of CloudControl. # # CloudControl is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # CloudControl is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with CloudControl. If not, see <http://www.gnu.org/licenses/>. No newline at end of file
cloudcontrol/common/allocation/vmspec.py 0 → 100644 +180 −0 Original line number Diff line number Diff line # This file is part of CloudControl. # # CloudControl is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # CloudControl is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with CloudControl. If not, see <http://www.gnu.org/licenses/>. import re class VMSpecValidationError(Exception): """ Exception raised when a VMSpec validation error occurs. """ # Checks: def check_type(object_, type_, message, **kwargs): if not isinstance(object_, type_): kwargs['type'] = type_.__class__.__name__ kwargs['object'] = str(object_) raise VMSpecValidationError(message % kwargs) def check_regex(object_, regex, message, **kwargs): if re.match(object_, regex): kwargs['object'] = str(object_) raise VMSpecValidationError(message % kwargs) def check_integer(object_, message, min=None, max=None, **kwargs): if not isinstance(object_, int): raise VMSpecValidationError(message % kwargs) elif (min is not None and object_ < min) or (max is not None and object_ > max): raise VMSpecValidationError(message % kwargs) def check_dict_key(dict_, key, message, **kwargs): if key not in dict_: kwargs['key'] = key raise VMSpecValidationError(message % kwargs) # Validation rules: def validate_riskgroup(data): """ Validate riskgroups. Example: {'name': {'tag': 1}} """ check_type(data, dict, 'riskgroup: must be a dict') for name, tags in data.iteritems(): env = {"name": name} check_type(name, basestring, 'riskgroup/[%(name)s]: must be a string', **env) check_type(tags, dict, 'riskgroup/%(name)s: must be a dict', **env) for tag, value in tags.iteritems(): check_type(tag, basestring, 'riskgroup/%(name)s/[%(tag)s]: must be a string', tag=tag, **env) check_integer(value, 'riskgroup/%(name)s/%(tag)s: must be a positive integer', min=1, tag=tag, **env) def validate_machine_volumes(hostname, data): """ Validate machines volumes. Example: {'root': {'size': 512000, 'pool': 'vg'}} """ check_type(data, list, 'machines/%(hostname)s/volumes: must be a list', hostname=hostname) for i, disk in enumerate(data): env = {'hostname': hostname, 'index': i} check_type(disk, dict, 'machines/%(hostname)s/volumes/%(index)s: must be a %(type)s', **env) check_dict_key(disk, 'size', 'machines/%(hostname)s/volumes/%(index)s/[%(key)s]: is mandatory', **env) check_integer(disk['size'], 'machines/%(hostname)ss/volumes/%(index)s/size: must be a positive integer', min=0, **env) check_dict_key(disk, 'pool', 'machines/%(hostname)s/volumes/%(index)s/[%(key)s]: is mandatory', **env) check_type(disk['pool'], basestring, 'machines/%(hostname)ss/volumes/%(index)s/pool: must be a string', **env) def validate_machine_spec(hostname, spec): """ Validate machines specifications. Example: {'cpu': 8, 'memory': 512000, 'flags': ['does_not_autostart'], 'tags': {'platform': 'Infra'}} """ env = {'hostname': hostname} check_integer(spec.get('cpu'), 'machines/%(hostname)s/cpu: must be a positive integer', min=1, **env) check_integer(spec.get('memory'), 'machines/%(hostname)s/memory: must be a positive integer', min=1, **env) if 'flags' in spec: if spec['flags'] is None: spec['flags'] = [] check_type(spec['flags'], list, 'machines/%(hostname)s/flags: must be a list', **env) for i, flag in enumerate(spec['flags']): check_type(flag, basestring, 'machines/%(hostname)s/flags[%(i)s]: must be a string', i=i, **env) if 'tags' in spec: if spec['tags'] is None: spec['tags'] = {} check_type(spec['tags'], dict, 'machines/%(hostname)s/tags: must be a dict', **env) for tag, value in spec['tags'].iteritems(): check_type(tag, basestring, 'machines/%(hostname)s/tags/[%(tag)s]: must be a string', tag=tag, **env) check_type(value, basestring, 'machines/%(hostname)s/tags/%(tag)s: must be a string', tag=tag, **env) if 'volumes' in spec: validate_machine_volumes(hostname, spec['volumes']) def validate_machines(data): """ Validate machine entry. Example: {'foobar-1.example.org': ...} """ check_type(data, dict, 'machines: must be a dict') if len(data) < 1: raise VMSpecValidationError('machines: at least one machine must be defined') for hostname, spec in data.iteritems(): if not re.match('^[a-z0-9-]+(.[a-z0-9-]+)*$', hostname): raise VMSpecValidationError('machines/[%s]: bad hostname format' % hostname) validate_machine_spec(hostname, spec) def validate_vmspec(data): """ Validate a vmspec. Example: {'riskgroups:' ..., 'machines': ...} """ if 'riskgroups' in data: validate_riskgroup(data['riskgroups']) check_dict_key(data, 'machines', 'machines: is mandatory') validate_machines(data['machines']) if 'target' in data: check_type(data['target'], basestring, 'target: must be a string (a TQL)') def expand_vmspec(vmspec): """ Expand the vmspec to a list of VM. """ validate_vmspec(vmspec) vmspec_expanded = [] riskgroups = vmspec.get('riskgroups', {}) for title, specs in vmspec['machines'].iteritems(): specs['title'] = title riskgroup = specs.get('tags', {}).get('riskgroup') if riskgroup is not None: if riskgroup not in riskgroups: raise VMSpecValidationError('machines/%s: riskgroup not defined') specs['riskgroup'] = riskgroups[riskgroup] vmspec_expanded.append(specs) return vmspec_expanded if __name__ == '__main__': import sys, json validate_vmspec(json.load(sys.stdin))