diff --git a/bareon/drivers/data/__init__.py b/bareon/drivers/data/__init__.py index e69de29..5572807 100644 --- a/bareon/drivers/data/__init__.py +++ b/bareon/drivers/data/__init__.py @@ -0,0 +1,48 @@ +# +# Copyright 2015 Cray Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import json + +import jsonschema.validators + +from bareon import errors + + +def validate(schema_path, payload): + schema = _load_validator_schema(schema_path) + + cls = jsonschema.validators.validator_for(schema) + cls.check_schema(schema) + schema_validator = cls(schema, format_checker=jsonschema.FormatChecker()) + + defects = schema_validator.iter_errors(payload) + defects = list(defects) + if defects: + raise errors.InputDataSchemaValidationError(defects) + + +def _load_validator_schema(schema_path): + try: + with open(schema_path, 'rt') as storage: + schema = json.load(storage) + except IOError as e: + raise errors.ApplicationDataCorruptError( + 'Can\'t read validation schema "{}": {} {}'.format( + e.filename, e.errno, e.strerror)) + except (ValueError, TypeError) as e: + raise errors.ApplicationDataCorruptError( + 'Corrupted validation schema "{}": {}'.format(schema_path, e)) + + return schema diff --git a/bareon/drivers/data/base.py b/bareon/drivers/data/base.py index 7ac9778..6487c86 100644 --- a/bareon/drivers/data/base.py +++ b/bareon/drivers/data/base.py @@ -14,9 +14,13 @@ import abc import copy +import os +import pkg_resources import six +import bareon.drivers.data + @six.add_metaclass(abc.ABCMeta) class BaseDataDriver(object): @@ -26,11 +30,21 @@ class BaseDataDriver(object): methods for getting object schemes, etc. """ + data_validation_schema = None + def __init__(self, data): + self.validate_data(data) self.data = copy.deepcopy(data) if 'flow' in self.data: self.flow = self.data['flow'] + @classmethod + def validate_data(cls, data): + root = pkg_resources.resource_filename( + 'bareon.drivers.data', 'json_schemes') + schema_path = os.path.join(root, cls.data_validation_schema) + bareon.drivers.data.validate(schema_path, data) + @six.add_metaclass(abc.ABCMeta) class PartitioningDataDriverMixin(object): @@ -82,3 +96,7 @@ class MultibootDeploymentMixin(object): @abc.abstractmethod def get_os_ids(self): pass + + @abc.abstractmethod + def _partition_data(self): + pass diff --git a/bareon/drivers/data/generic.py b/bareon/drivers/data/generic.py index 3f47fee..c9a0e12 100644 --- a/bareon/drivers/data/generic.py +++ b/bareon/drivers/data/generic.py @@ -12,11 +12,14 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. + import os from oslo_config import cfg from bareon import errors +from bareon.utils.partition import MiB +from bareon.utils.partition import TiB from bareon.utils import utils from bareon.drivers.data.base import BaseDataDriver @@ -167,7 +170,7 @@ class GenericDataDriver(BaseDataDriver, @property def _small_ks_disks(self): """Get those disks which are smaller than 2T""" - return [d for d in self._ks_disks if d['size'] <= 2 * 1024 * 1024] + return [x for x in self._ks_disks if x['size'] <= 2 * TiB / MiB] def get_os_ids(self): raise NotImplementedError diff --git a/bareon/drivers/data/ironic.py b/bareon/drivers/data/ironic.py index 8cb87d2..33da192 100644 --- a/bareon/drivers/data/ironic.py +++ b/bareon/drivers/data/ironic.py @@ -22,7 +22,6 @@ from oslo_config import cfg from oslo_log import log as logging from bareon.drivers.data.generic import GenericDataDriver -from bareon.drivers.data import ks_spaces_validator from bareon import errors from bareon import objects from bareon.utils import hardware as hu @@ -39,11 +38,14 @@ DEFAULT_GRUB_SIZE = 24 class Ironic(GenericDataDriver): + data_validation_schema = 'ironic.json' + + _root_on_lvm = None + _boot_on_lvm = None def __init__(self, data): super(Ironic, self).__init__(data) - self._root_on_lvm = None - self._boot_on_lvm = None + convert_size(self.data['partitions']) def _get_image_meta(self): pass @@ -120,15 +122,11 @@ class Ironic(GenericDataDriver): scanning/comparing the underlying node hardware. """ LOG.debug('--- Preparing partition scheme ---') - # TODO(oberezovskyi): make validator work - data = self._partition_data() - ks_spaces_validator.validate(data, 'ironic') - data = convert_size(data) - partition_schema = objects.PartitionScheme() + LOG.debug('Looping over all disks in provision data') multiboot_installed = False - LOG.debug('Looping over all disks in provision data') + partition_schema = objects.PartitionScheme() for disk in self._ks_disks: # # skipping disk if there are no volumes with size >0 # # to be allocated on it which are not boot partitions @@ -602,6 +600,18 @@ class Ironic(GenericDataDriver): else: return fnmatch.fnmatch(hu_data.get(id_type, ''), id_value) + @classmethod + def validate_data(cls, data): + super(Ironic, cls).validate_data(data) + + disks = data['partitions'] + + # scheme is not valid if the number of disks is 0 + if not [d for d in disks if d['type'] == 'disk']: + raise errors.WrongInputDataError( + 'Invalid partition schema: You must specify at least one ' + 'disk.') + def convert_size(data): data = convert_string_sizes(data) diff --git a/bareon/drivers/data/json_schemes/ironic.json b/bareon/drivers/data/json_schemes/ironic.json index 320504f..3c59e3e 100644 --- a/bareon/drivers/data/json_schemes/ironic.json +++ b/bareon/drivers/data/json_schemes/ironic.json @@ -1,54 +1,223 @@ { - "uniqueItems": true, "$schema": "http://json-schema.org/draft-04/schema#", - "type": "array", - "title": "Ironic partition schema", - "minItems": 1, - "items": { - "anyOf": [ - { - "required": [ - "type", - "id", - "volumes", - "size" - ], + "title": "Ironic deployment config schema", + "type": "object", + "required": [ + "partitions", + "images" + ], + "properties": { + "images": { + "type": "array", + "items": { "type": "object", "properties": { + "image_name": { + "type": "string" + }, + "image_uuid": { + "type": "string" + }, + "image_pull_url": { + "type": "string" + }, + "target": { + "type": "string" + }, "name": { "type": "string" }, - "volumes": { - "items": { - "anyOf": [ - { + "boot": { + "oneOf": [ + { + "type": "boolean" + }, + { + "type": "integer" + } + ] + } + } + } + }, + "image_deploy_flags": { + "type": "object" + }, + "partitions_policy": { + "enum": [ + "verify", + "clean", + "nailgun_legacy" + ] + }, + "partitions": { + "type": "array", + "uniqueItems": true, + "minItems": 1, + "items": { + "anyOf": [ + { + "type": "object", + "required": [ + "type", + "id", + "volumes", + "size" + ], + "properties": { + "name": { + "type": "string" + }, + "volumes": { + "items": { + "anyOf": [ + { + "required": [ + "type", + "size", + "vg" + ], + "type": "object", + "properties": { + "vg": { + "type": "string" + }, + "type": { + "enum": [ + "pv" + ] + }, + "keep_data": { + "type": "boolean" + }, + "lvm_meta_size": { + "type": "string" + }, + "size": { + "type": "string" + } + } + }, + { + "required": [ + "type", + "size" + ], + "type": "object", + "properties": { + "mount": { + "type": "string" + }, + "type": { + "enum": [ + "raid", + "partition" + ] + }, + "keep_data": { + "type": "boolean" + }, + "file_system": { + "type": "string" + }, + "name": { + "type": "string" + }, + "size": { + "type": "string" + } + } + }, + { + "required": [ + "type", + "size" + ], + "type": "object", + "properties": { + "type": { + "enum": [ + "boot" + ] + }, + "size": { + "type": "string" + }, + "keep_data": { + "type": "boolean" + } + } + }, + { + "required": [ + "type", + "size" + ], + "type": "object", + "properties": { + "type": { + "enum": [ + "lvm_meta_pool" + ] + }, + "size": { + "type": "string" + } + } + } + ] + }, + "type": "array" + }, + "type": { + "enum": [ + "disk" + ] + }, + "id": { + "required": [ + "type", + "value" + ], + "type": "object", + "properties": { + "type": { + "enum": [ + "scsi", + "path", + "name" + ] + } + } + }, + "size": { + "type": "string" + } + } + }, + { + "required": [ + "type", + "id", + "volumes" + ], + "type": "object", + "properties": { + "_allocate_size": { + "type": "string" + }, + "label": { + "type": "string" + }, + "min_size": { + "type": "integer" + }, + "volumes": { + "items": { "required": [ "type", "size", - "vg" - ], - "type": "object", - "properties": { - "vg": { - "type": "string" - }, - "type": { - "enum": [ - "pv" - ] - }, - "lvm_meta_size": { - "type": "string" - }, - "size": { - "type": "string" - } - } - }, - { - "required": [ - "type", - "size" + "name" ], "type": "object", "properties": { @@ -57,142 +226,48 @@ }, "type": { "enum": [ - "raid", - "partition" + "lv" ] }, - "file_system": { - "type": "string" + "keep_data": { + "type": "boolean" }, "name": { "type": "string" }, - "size": { + "file_system": { "type": "string" - } - } - }, - { - "required": [ - "type", - "size" - ], - "type": "object", - "properties": { - "type": { - "enum": [ - "boot" - ] }, "size": { "type": "string" } } }, - { - "required": [ - "type", - "size" - ], - "type": "object", - "properties": { - "type": { - "enum": [ - "lvm_meta_pool" - ] - }, - "size": { - "type": "string" - } - } - } - ] - }, - "type": "array" - }, - "type": { - "enum": [ - "disk" - ] - }, - "id": { - "required": [ - "type", - "value" - ], - "type": "object", - "properties": { + "type": "array" + }, "type": { "enum": [ - "scsi", - "path", - "name" + "vg" ] + }, + "keep_data": { + "type": "boolean" + }, + "id": { + "type": "string" } } - }, - "size": { - "type": "string" - } - } - }, - { - "required": [ - "type", - "id", - "volumes" - ], - "type": "object", - "properties": { - "_allocate_size": { - "type": "string" - }, - "label": { - "type": "string" - }, - "min_size": { - "type": "integer" - }, - "volumes": { - "items": { - "required": [ - "type", - "size", - "name" - ], - "type": "object", - "properties": { - "mount": { - "type": "string" - }, - "type": { - "enum": [ - "lv" - ] - }, - "name": { - "type": "string" - }, - "file_system": { - "type": "string" - }, - "size": { - "type": "string" - } - } - }, - "type": "array" - }, - "type": { - "enum": [ - "vg" - ] - }, - "id": { - "type": "string" } + ] + } + }, + "deploy_data": { + "type": "object", + "properties": { + "kernel_params": { + "type": "string" } } - ] + } } -} \ No newline at end of file +} diff --git a/bareon/drivers/data/json_schemes/nailgun-build-image.json b/bareon/drivers/data/json_schemes/nailgun-build-image.json new file mode 100644 index 0000000..e6307dc --- /dev/null +++ b/bareon/drivers/data/json_schemes/nailgun-build-image.json @@ -0,0 +1,3 @@ +{ + "type": "object" +} diff --git a/bareon/drivers/data/json_schemes/nailgun.json b/bareon/drivers/data/json_schemes/nailgun.json index 32a73cd..d15097d 100644 --- a/bareon/drivers/data/json_schemes/nailgun.json +++ b/bareon/drivers/data/json_schemes/nailgun.json @@ -1,99 +1,314 @@ { - "$schema": "http://json-schema.org/draft-04/schema#", - "title": "Partition scheme", - "type": "array", - "minItems": 1, - "uniqueItems": true, - "items": { - "anyOf": [ - { - "type": "object", - "required": ["type", "id", "volumes", "name", - "size", "extra", "free_space"], - "properties": { - "type": {"enum": ["disk"]}, - "id": {"type": "string"}, - "name": {"type": "string"}, - "size": {"type": "integer"}, - "free_space": {"type": "integer"}, - "extra": { - "type": "array", - "items": {"type": "string"} - }, - "volumes": { - "type": "array", - "items": { + "title": "Nailgun partition schema", + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "required": [ + "ks_meta" + ], + "properties": { + "hostname": { + "type": "string" + }, + "name_servers": { + "type": "string" + }, + "name_servers_search": { + "type": "string" + }, + "profile": { + "type": "string" + }, + "ks_meta": { + "type": "object", + "required": [ + "pm_data" + ], + "properties": { + "timezone": { + "type": "string" + }, + "master_ip": { + "type": "string" + }, + "gw": { + "type": "string" + }, + "auth_key": { + "type": "string" + }, + "pm_data": { + "type": "object", + "required": [ + "ks_spaces" + ], + "properties": { + "kernel_params": { + "type": "string" + }, + "ks_spaces": { + "type": "array", + "uniqueItems": true, + "minItems": 1, + "items": { + "anyOf": [ + { + "required": [ + "type", + "id", + "volumes", + "name", + "size", + "extra", + "free_space" + ], + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "extra": { + "items": { + "type": "string" + }, + "type": "array" + }, + "free_space": { + "type": "integer" + }, + "volumes": { + "items": { "anyOf": [ - { - "type": "object", - "required": ["type", "size", - "lvm_meta_size", "vg"], - "properties": { - "type": {"enum": ["pv"]}, - "size": {"type": "integer"}, - "lvm_meta_size": {"type": "integer"}, - "vg": {"type": "string"} - } - }, - { - "type": "object", - "required": ["type", "size"], - "properties": { - "type": {"enum": ["raid", - "partition"]}, - "size": {"type": "integer"}, - "mount": {"type": "string"}, - "file_system": {"type": "string"}, - "name": {"type": "string"} - } - }, - { - "type": "object", - "required": ["type", "size"], - "properties": { - "type": {"enum": ["boot"]}, - "size": {"type": "integer"} - } - }, - { - "type": "object", - "required": ["type", "size"], - "properties": { - "type": {"enum": ["lvm_meta_pool"]}, - "size": {"type": "integer"} - } + { + "required": [ + "type", + "size", + "vg" + ], + "type": "object", + "properties": { + "vg": { + "type": "string" + }, + "type": { + "enum": [ + "pv" + ] + }, + "lvm_meta_size": { + "type": "integer" + }, + "size": { + "type": "integer" + } } - + }, + { + "required": [ + "type", + "size" + ], + "type": "object", + "properties": { + "mount": { + "type": "string" + }, + "type": { + "enum": [ + "raid", + "partition" + ] + }, + "file_system": { + "type": "string" + }, + "name": { + "type": "string" + }, + "size": { + "type": "integer" + } + } + }, + { + "required": [ + "type", + "size" + ], + "type": "object", + "properties": { + "type": { + "enum": [ + "boot" + ] + }, + "size": { + "type": "integer" + } + } + }, + { + "required": [ + "type", + "size" + ], + "type": "object", + "properties": { + "type": { + "enum": [ + "lvm_meta_pool" + ] + }, + "size": { + "type": "integer" + } + } + } ] + }, + "type": "array" + }, + "type": { + "enum": [ + "disk" + ] + }, + "id": { + "type": "string" + }, + "size": { + "type": "integer" } + } + }, + { + "required": [ + "type", + "id", + "volumes" + ], + "type": "object", + "properties": { + "_allocate_size": { + "type": "string" + }, + "label": { + "type": "string" + }, + "min_size": { + "type": "integer" + }, + "volumes": { + "items": { + "required": [ + "type", + "size", + "name" + ], + "type": "object", + "properties": { + "mount": { + "type": "string" + }, + "type": { + "enum": [ + "lv" + ] + }, + "name": { + "type": "string" + }, + "file_system": { + "type": "string" + }, + "size": { + "type": "integer" + } + } + }, + "type": "array" + }, + "type": { + "enum": [ + "vg" + ] + }, + "id": { + "type": "string" + } + } } + ] } + } + } + }, + "image_data": { + "type": "object" + }, + "repo_setup": { + "type": "object", + "properties": { + "repos": { + "type": "array" + } + } + }, + "puppet_master": { + "type": "string" + }, + "puppet_enable": { + "oneOf": [ + { + "type": "boolean" }, { - "type": "object", - "required": ["type", "id", "volumes"], - "properties": { - "type": {"enum": ["vg"]}, - "id": {"type": "string"}, - "label": {"type": "string"}, - "min_size": {"type": "integer"}, - "_allocate_size": {"type": "string"}, - "volumes": { - "type": "array", - "items": { - "type": "object", - "required": ["type", "size", "name"], - "properties": { - "type": {"enum": ["lv"]}, - "size": {"type": "integer"}, - "name": {"type": "string"}, - "mount": {"type": "string"}, - "file_system": {"type": "string"} - } - } - } - } + "type": "integer" } - ] + ] + }, + "mco_pskey": { + "type": "string" + }, + "mco_vhost": { + "type": "string" + }, + "mco_host": { + "type": "string" + }, + "mco_user": { + "type": "string" + }, + "mco_password": { + "type": "string" + }, + "mco_connector": { + "type": "string" + }, + "mco_enable": { + "oneOf": [ + { + "type": "boolean" + }, + { + "type": "integer" + } + ] + } + } + }, + "kernel_options": { + "type": "object", + "properties": { + "netcfg/choose_interface": { + "type": "string" + }, + "udevrules": { + "type": "string" + } + } + }, + "interfaces": { + "type": "object" } + } } diff --git a/bareon/drivers/data/ks_spaces_validator.py b/bareon/drivers/data/ks_spaces_validator.py deleted file mode 100644 index c5796e2..0000000 --- a/bareon/drivers/data/ks_spaces_validator.py +++ /dev/null @@ -1,57 +0,0 @@ -# Copyright 2014 Mirantis, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import json -import jsonschema -import os - -from oslo_log import log as logging - -from bareon import errors - -LOG = logging.getLogger(__name__) - - -def validate(data, schema_file='nailgun'): - """Validates a given partition scheme using jsonschema. - - :param scheme: partition scheme to validate - """ - base_path = os.path.dirname(__file__) - schemas_path = os.path.join(base_path, 'json_schemes') - with open(os.path.join(schemas_path, '%s.json' % schema_file)) as file: - schema = json.load(file) - - try: - checker = jsonschema.FormatChecker() - jsonschema.validate(data, schema, - format_checker=checker) - except Exception as exc: - LOG.exception(exc) - raise errors.WrongPartitionSchemeError(str(exc)) - - # scheme is not valid if the number of disks is 0 - if not [d for d in data if d['type'] == 'disk']: - raise errors.WrongPartitionSchemeError( - 'Partition scheme seems empty') - - # TODO(lobur): Must be done after unit conversion - # for space in data: - # for volume in space.get('volumes', []): - # if volume['size'] > 16777216 and volume.get('mount') == '/': - # raise errors.WrongPartitionSchemeError( - # 'Root file system must be less than 16T') - - # TODO(kozhukalov): need to have additional logical verifications - # maybe sizes and format of string values diff --git a/bareon/drivers/data/nailgun.py b/bareon/drivers/data/nailgun.py index 16a8905..b0263f5 100644 --- a/bareon/drivers/data/nailgun.py +++ b/bareon/drivers/data/nailgun.py @@ -32,7 +32,6 @@ from bareon.drivers.data.base import ConfigDriveDataDriverMixin from bareon.drivers.data.base import GrubBootloaderDataDriverMixin from bareon.drivers.data.base import PartitioningDataDriverMixin from bareon.drivers.data.base import ProvisioningDataDriverMixin -from bareon.drivers.data import ks_spaces_validator from bareon import errors from bareon import objects @@ -84,6 +83,9 @@ class Nailgun(BaseDataDriver, GrubBootloaderDataDriverMixin): """Driver for parsing regular volumes metadata from Nailgun.""" + data_validation_schema = 'nailgun.json' + partitions_policy = 'nailgun_legacy' + def __init__(self, data): super(Nailgun, self).__init__(data) @@ -336,15 +338,13 @@ class Nailgun(BaseDataDriver, def parse_partition_scheme(self): LOG.debug('--- Preparing partition scheme ---') - data = self.partition_data() - ks_spaces_validator.validate(data) - partition_scheme = objects.PartitionScheme() ceph_osds = self._num_ceph_osds() journals_left = ceph_osds ceph_journals = self._num_ceph_journals() LOG.debug('Looping over all disks in provision data') + partition_scheme = objects.PartitionScheme() for disk in self.ks_disks: # skipping disk if there are no volumes with size >0 # to be allocated on it which are not boot partitions @@ -710,6 +710,18 @@ class Nailgun(BaseDataDriver, ) return image_scheme + @classmethod + def validate_data(cls, data): + super(Nailgun, cls).validate_data(data) + + disks = data['ks_meta']['pm_data']['ks_spaces'] + + # scheme is not valid if the number of disks is 0 + if not [d for d in disks if d['type'] == 'disk']: + raise errors.WrongInputDataError( + 'Invalid partition schema: You must specify at least one ' + 'disk.') + class Ironic(Nailgun): def __init__(self, data): @@ -723,6 +735,7 @@ class NailgunBuildImage(BaseDataDriver, ProvisioningDataDriverMixin, ConfigDriveDataDriverMixin, GrubBootloaderDataDriverMixin): + data_validation_schema = 'nailgun-build-image.json' DEFAULT_TRUSTY_PACKAGES = [ "acl", diff --git a/bareon/errors.py b/bareon/errors.py index 22edbb1..ffdd31e 100644 --- a/bareon/errors.py +++ b/bareon/errors.py @@ -19,10 +19,32 @@ class BaseError(Exception): super(BaseError, self).__init__(message, *args, **kwargs) +class ApplicationDataCorruptError(BaseError): + pass + + class WrongInputDataError(BaseError): pass +class InputDataSchemaValidationError(WrongInputDataError): + def __init__(self, defects): + human_readable_defects = [] + for idx, d in enumerate(defects): + path = [''] + list(d.path) + path = '/'.join((str(x) for x in path)) + human_readable_defects.append( + '#{} ({}): {}'.format(idx, path, d.message)) + + indent = ' ' * 4 + separator = '\n{}'.format(indent) + message = 'Invalid input data:\n{}{}'.format( + indent, separator.join(human_readable_defects)) + + super(WrongInputDataError, self).__init__(message, defects) + self.defects = defects + + class WrongPartitionSchemeError(BaseError): pass diff --git a/bareon/tests/fixtures/simple_nailgun_driver.json b/bareon/tests/fixtures/simple_nailgun_driver.json index a85e2ca..c30aca2 100644 --- a/bareon/tests/fixtures/simple_nailgun_driver.json +++ b/bareon/tests/fixtures/simple_nailgun_driver.json @@ -63,7 +63,17 @@ "mco_identity": -1, "pm_data": { "kernel_params": "console=ttyS0,9600 console=tty0 rootdelay=90 nomodeset", - "ks_spaces": [] + "ks_spaces": [ + { + "type": "disk", + "id": "dummy-disk-id", + "name": "dummy0", + "free_space": 0, + "size": 128, + "extra": [], + "volumes": [] + } + ] }, "puppet_auto_setup": 1, "puppet_enable": 0, diff --git a/bareon/tests/test_generic_data_driver.py b/bareon/tests/test_generic_data_driver.py index 56366d0..3a648a9 100644 --- a/bareon/tests/test_generic_data_driver.py +++ b/bareon/tests/test_generic_data_driver.py @@ -17,12 +17,14 @@ import mock import unittest2 from bareon.drivers.data import generic +from bareon.utils.partition import MiB +from bareon.utils.partition import TiB class TestKsDisks(unittest2.TestCase): - def __init__(self, *args, **kwargs): - super(TestKsDisks, self).__init__(*args, **kwargs) - self.driver = generic.GenericDataDriver({}) + def setUp(self): + super(TestKsDisks, self).setUp() + self.driver = _DummyDataDriver({}) self.driver._partition_data = self.mock_part_data = mock.MagicMock() def test_no_partition_data(self): @@ -72,9 +74,9 @@ class TestKsDisks(unittest2.TestCase): class TestKsVgs(unittest2.TestCase): - def __init__(self, *args, **kwargs): - super(TestKsVgs, self).__init__(*args, **kwargs) - self.driver = generic.GenericDataDriver({}) + def setUp(self): + super(TestKsVgs, self).setUp() + self.driver = _DummyDataDriver({}) self.driver._partition_data = self.mock_part_data = mock.MagicMock() def test_no_partition_data(self): @@ -112,9 +114,9 @@ class TestKsVgs(unittest2.TestCase): class TestSmallKsDisks(unittest2.TestCase): - def __init__(self, *args, **kwargs): - super(TestSmallKsDisks, self).__init__(*args, **kwargs) - self.driver = generic.GenericDataDriver({}) + def setUp(self): + super(TestSmallKsDisks, self).setUp() + self.driver = _DummyDataDriver({}) self.driver._partition_data = self.mock_part_data = mock.MagicMock() def test_no_partition_data(self): @@ -128,8 +130,8 @@ class TestSmallKsDisks(unittest2.TestCase): def test_no_partitions_valid_size(self): self.mock_part_data.return_value = [ - {'size': 3 * 1024 * 1024, 'type': 'disk'}, - {'size': 5 * 1024 * 1024, 'type': 'disk'} + {'size': 3 * TiB, 'type': 'disk'}, + {'size': 5 * TiB, 'type': 'disk'} ] desired = [] @@ -140,10 +142,10 @@ class TestSmallKsDisks(unittest2.TestCase): def test_valid_data(self): self.mock_part_data.return_value = [ - {'size': 3 * 1024 * 1024, 'type': 'vg'}, - {'size': 1 * 1024 * 1024, 'type': 'disk'} + {'size': 3 * MiB, 'type': 'vg'}, + {'size': 1 * MiB, 'type': 'disk'} ] - desired = [{'size': 1 * 1024 * 1024, 'type': 'disk'}] + desired = [{'size': 1 * MiB, 'type': 'disk'}] result = self.driver._small_ks_disks @@ -152,9 +154,9 @@ class TestSmallKsDisks(unittest2.TestCase): class TestGetLabel(unittest2.TestCase): - def __init__(self, *args, **kwargs): - super(TestGetLabel, self).__init__(*args, **kwargs) - self.driver = generic.GenericDataDriver({}) + def setUp(self): + super(TestGetLabel, self).setUp() + self.driver = _DummyDataDriver({}) def test_no_label(self): label = None @@ -179,3 +181,12 @@ class TestGetLabel(unittest2.TestCase): result = self.driver._getlabel(label) self.assertEqual(result, desired) + + +class _DummyDataDriver(generic.GenericDataDriver): + def _partition_data(self): + return [] + + @classmethod + def validate_data(cls, payload): + pass diff --git a/bareon/tests/test_ironic.py b/bareon/tests/test_ironic.py index 8c4010e..becd689 100644 --- a/bareon/tests/test_ironic.py +++ b/bareon/tests/test_ironic.py @@ -226,11 +226,14 @@ LIST_BLOCK_DEVICES_SAMPLE = [ ] -class TestIronicMatch(unittest2.TestCase): - def __init__(self, *args, **kwargs): - super(TestIronicMatch, self).__init__(*args, **kwargs) - self.data_driver = ironic.Ironic('') +class _IronicTest(unittest2.TestCase): + def setUp(self): + super(_IronicTest, self).setUp() + with mock.patch.object(ironic.Ironic, 'validate_data'): + self.data_driver = ironic.Ironic({'partitions': []}) + +class TestIronicMatch(_IronicTest): def test_match_device_by_scsi_matches(self): # matches by scsi address fake_ks_disk = { @@ -319,10 +322,11 @@ class TestIronicMatch(unittest2.TestCase): self.data_driver._match_device(fake_hu_disk, fake_ks_disk)) -@mock.patch('bareon.drivers.data.ironic.hu.scsi_address') -class TestNailgunMockedMeta(unittest2.TestCase): - def test_partition_scheme(self, mock_scsi_address): - data_driver = ironic.Ironic(PROVISION_SAMPLE_DATA_SWIFT) +@mock.patch('bareon.utils.hardware.scsi_address', mock.Mock()) +class TestNailgunMockedMeta(_IronicTest): + def test_partition_scheme(self): + with mock.patch.object(ironic.Ironic, 'validate_data'): + data_driver = ironic.Ironic(PROVISION_SAMPLE_DATA_SWIFT) data_driver.get_image_ids = mock.MagicMock mock_devices = data_driver._get_block_devices = mock.MagicMock() @@ -336,18 +340,17 @@ class TestNailgunMockedMeta(unittest2.TestCase): self.assertEqual(3, len(p_scheme.parteds)) -@mock.patch('bareon.drivers.data.ironic.hu.get_block_devices_from_udev_db') -class TestGetBlockDevices(unittest2.TestCase): - def __init__(self, *args, **kwargs): - super(TestGetBlockDevices, self).__init__(*args, **kwargs) - self.driver = ironic.Ironic('') - self.mock_devices = mock.MagicMock() - self.driver._get_block_device_info = self.mock_devices +@mock.patch('bareon.utils.hardware.get_block_devices_from_udev_db') +class TestGetBlockDevices(_IronicTest): + def setUp(self): + super(TestGetBlockDevices, self).setUp() + self.mock_devices = mock.Mock() + self.data_driver._get_block_device_info = self.mock_devices def test_no_devices(self, mock_get_block_devices_from_udev_db): mock_get_block_devices_from_udev_db.return_value = [] - result = self.driver._get_block_devices() + result = self.data_driver._get_block_devices() self.assertEqual(result, []) mock_get_block_devices_from_udev_db.assert_called_once_with() self.assertEqual(self.mock_devices.call_count, 0) @@ -357,32 +360,30 @@ class TestGetBlockDevices(unittest2.TestCase): mock_get_block_devices_from_udev_db.return_value = [data] self.mock_devices.return_value = block_device = 'test_value' - result = self.driver._get_block_devices() + result = self.data_driver._get_block_devices() self.assertEqual(result, [block_device]) mock_get_block_devices_from_udev_db.assert_called_once_with() self.mock_devices.assert_called_once_with(data) -@mock.patch('bareon.drivers.data.ironic.hu.get_device_ids') -@mock.patch('bareon.drivers.data.ironic.hu.get_device_info') -@mock.patch('bareon.drivers.data.ironic.hu.scsi_address') -class TestGetBlockDevice(unittest2.TestCase): +@mock.patch('bareon.utils.hardware.get_device_ids') +@mock.patch('bareon.utils.hardware.get_device_info') +@mock.patch('bareon.utils.hardware.scsi_address') +class TestGetBlockDevice(_IronicTest): def test_no_device_info(self, mock_scsi_address, mock_get_device_info, mock_get_device_ids): - data_driver = ironic.Ironic('') device = 'fake_device' mock_scsi_address.return_value = None mock_get_device_info.return_value = {} mock_get_device_ids.return_value = [] - result = data_driver._get_block_device_info(device) + result = self.data_driver._get_block_device_info(device) self.assertEqual(result, {'name': 'fake_device'}) def test_device_info(self, mock_scsi_address, mock_get_device_info, mock_get_device_ids): - data_driver = ironic.Ironic('') device = 'fake_device' devpath = ['test/devpath'] uspec = {'DEVPATH': devpath} @@ -396,19 +397,19 @@ class TestGetBlockDevice(unittest2.TestCase): desired = {'path': devpath, 'name': device, 'scsi': scsi_address, 'uspec': uspec} - result = data_driver._get_block_device_info(device) + result = self.data_driver._get_block_device_info(device) self.assertEqual(result, desired) mock_get_device_info.assert_called_once_with(device) mock_scsi_address.assert_called_once_with(device) +@mock.patch('bareon.drivers.data.ironic.Ironic.validate_data', mock.Mock()) class TestGetGrub(unittest2.TestCase): - @mock.patch('bareon.utils.utils.parse_kernel_cmdline') def test_kernel_params(self, cmdline_mock): data = {'deploy_data': {'kernel_params': "test_param=test_val", 'other_data': 'test'}, - 'partitions': 'fake_shema'} + 'partitions': {}} cmdline_mock.return_value = { "BOOTIF": "01-52-54-00-a5-55-58", "extrastuff": "test123" @@ -421,24 +422,24 @@ class TestGetGrub(unittest2.TestCase): def test_no_kernel_params(self): data = {'deploy_data': {'other_data': "test"}, - 'partitions': 'fake_shema'} + 'partitions': {}} data_driver = ironic.Ironic(data) self.assertEqual('', data_driver.grub.kernel_params) +@mock.patch('bareon.drivers.data.ironic.Ironic.validate_data', mock.Mock()) class TestPartitionsPolicy(unittest2.TestCase): - def test_partitions_policy(self): data = {'partitions_policy': "test_value", - 'partitions': 'fake_shema'} + 'partitions': {}} data_driver = ironic.Ironic(data) self.assertEqual('test_value', data_driver.partitions_policy) def test_partitions_policy_default(self): - data = {'partitions': 'fake_shema'} + data = {'partitions': {}} data_driver = ironic.Ironic(data) diff --git a/bareon/tests/test_ironic_data_driver.py b/bareon/tests/test_ironic_data_driver.py index 3b09341..e448534 100644 --- a/bareon/tests/test_ironic_data_driver.py +++ b/bareon/tests/test_ironic_data_driver.py @@ -23,10 +23,24 @@ from bareon.utils import hardware as hu from bareon.utils import utils -class TestGetImageSchema(unittest2.TestCase): - def __init__(self, *args, **kwargs): - super(TestGetImageSchema, self).__init__(*args, **kwargs) +class IronicTestAbstract(unittest2.TestCase): + def setUp(self): + super(IronicTestAbstract, self).setUp() + self.driver = self.new_data_driver() + @staticmethod + def new_data_driver(payload=None): + if payload is None: + payload = {} + payload.setdefault('partitions', []) + + with mock.patch.object(ironic.Ironic, 'validate_data'): + driver = ironic.Ironic(payload) + + return driver + + +class TestGetImageSchema(IronicTestAbstract): def test_get_image_schema(self): image_uri = 'test_uri' rsync_flags = '-a -X' @@ -38,9 +52,9 @@ class TestGetImageSchema(unittest2.TestCase): 'name': 'test' } ], 'image_deploy_flags': deploy_flags} - self.driver = ironic.Ironic(data) + driver = self.new_data_driver(data) - result = self.driver._get_image_scheme() + result = driver._get_image_scheme() self.assertEqual(len(result.images), 1) @@ -49,11 +63,7 @@ class TestGetImageSchema(unittest2.TestCase): self.assertEqual(result_image.uri, image_uri) -class TestMatchDevice(unittest2.TestCase): - def __init__(self, *args, **kwargs): - super(TestMatchDevice, self).__init__(*args, **kwargs) - self.driver = ironic.Ironic({}) - +class TestMatchDevice(IronicTestAbstract): def test_match_list_value(self): test_type = 'path' test_value = 'test_path' @@ -95,10 +105,9 @@ class TestMatchDevice(unittest2.TestCase): self.assertFalse(result) -class TestDiskDev(unittest2.TestCase): - def __init__(self, *args, **kwargs): - super(TestDiskDev, self).__init__(*args, **kwargs) - self.driver = ironic.Ironic({}) +class TestDiskDev(IronicTestAbstract): + def setUp(self): + super(TestDiskDev, self).setUp() self.driver._match_device = self.mock_match_device = mock.MagicMock() def test_no_valid_disks(self): @@ -133,11 +142,7 @@ class TestDiskDev(unittest2.TestCase): self.assertEqual(result, 'disk1') -class TestMatchPartition(unittest2.TestCase): - def __init__(self, *args, **kwargs): - super(TestMatchPartition, self).__init__(*args, **kwargs) - self.driver = ironic.Ironic({}) - +class TestMatchPartition(IronicTestAbstract): def test_match_list_value(self): test_type = 'path' test_value = 'test_path' @@ -221,10 +226,9 @@ class TestMatchPartition(unittest2.TestCase): self.assertFalse(result) -class TestDiskPartition(unittest2.TestCase): - def __init__(self, *args, **kwargs): - super(TestDiskPartition, self).__init__(*args, **kwargs) - self.driver = ironic.Ironic({}) +class TestDiskPartition(IronicTestAbstract): + def setUp(self): + super(TestDiskPartition, self).setUp() self.driver._match_data_by_pattern = \ self.mock_match_part = mock.MagicMock() @@ -262,11 +266,7 @@ class TestDiskPartition(unittest2.TestCase): @mock.patch('bareon.utils.hardware.get_partitions_from_udev_db') @mock.patch('bareon.utils.hardware.get_device_ids') -class TestGetPartitionIds(unittest2.TestCase): - def __init__(self, *args, **kwargs): - super(TestGetPartitionIds, self).__init__(*args, **kwargs) - self.driver = ironic.Ironic({}) - +class TestGetPartitionIds(IronicTestAbstract): def test_no_devices(self, mock_ids, mock_partitions): mock_partitions.return_value = [] desired = [] @@ -299,22 +299,21 @@ class TestGetPartitionIds(unittest2.TestCase): mock_ids.assert_has_calls([mock.call(part) for part in parts]) -class TestFindHwFstab(unittest2.TestCase): +class TestFindHwFstab(IronicTestAbstract): @mock.patch.object(utils, 'execute') def test__find_hw_fstab_success_single_disk(self, exec_mock): fs = namedtuple('fs', 'mount type device os_id') fss = [fs(mount='/', type='ext4', device='/dev/sda', os_id='1'), fs(mount='/usr', type='ext4', device='/dev/sdb', os_id='1')] - data_driver = ironic.Ironic({}) - data_driver._partition_scheme = ironic.objects.PartitionScheme() - data_driver.partition_scheme.fss = fss + self.driver._partition_scheme = ironic.objects.PartitionScheme() + self.driver.partition_scheme.fss = fss exec_mock.side_effect = [('stdout', 'stderr'), ('fstab_1', 'stderr'), ('stdout', 'stderr')] - res = data_driver._find_hw_fstab() + res = self.driver._find_hw_fstab() self.assertEqual('\n'.join(('fstab_1',)), res) @@ -325,9 +324,8 @@ class TestFindHwFstab(unittest2.TestCase): fs(mount='/usr', type='ext4', device='/dev/sdb', os_id='1'), fs(mount='/', type='ext4', device='/dev/sda', os_id='2')] - data_driver = ironic.Ironic({}) - data_driver._partition_scheme = ironic.objects.PartitionScheme() - data_driver.partition_scheme.fss = fss + self.driver._partition_scheme = ironic.objects.PartitionScheme() + self.driver.partition_scheme.fss = fss exec_mock.side_effect = [('stdout', 'stderr'), ('fstab_1', 'stderr'), @@ -336,7 +334,7 @@ class TestFindHwFstab(unittest2.TestCase): ('fstab_2', 'stderr'), ('stdout', 'stderr')] - res = data_driver._find_hw_fstab() + res = self.driver._find_hw_fstab() self.assertEqual('\n'.join(('fstab_1', 'fstab_2')), res) @@ -346,15 +344,14 @@ class TestFindHwFstab(unittest2.TestCase): fss = [fs(mount='/etc', type='ext4', device='/dev/sda', os_id='1'), fs(mount='/', type='ext4', device='/dev/sda', os_id='1')] - data_driver = ironic.Ironic({}) - data_driver._partition_scheme = ironic.objects.PartitionScheme() - data_driver.partition_scheme.fss = fss + self.driver._partition_scheme = ironic.objects.PartitionScheme() + self.driver.partition_scheme.fss = fss exec_mock.side_effect = [('stdout', 'stderr'), errors.ProcessExecutionError, ('stdout', 'stderr')] self.assertRaises(errors.HardwarePartitionSchemeCannotBeReadError, - data_driver._find_hw_fstab) + self.driver._find_hw_fstab) class TestConvertStringSize(unittest2.TestCase): @@ -847,10 +844,9 @@ class TestConvertPercentSizes(unittest2.TestCase): map(lambda r, d: self.assertDictEqual(r, d), result, desired) -class TestProcessPartition(unittest2.TestCase): - def __init__(self, *args, **kwargs): - super(TestProcessPartition, self).__init__(*args, **kwargs) - self.driver = ironic.Ironic({}) +class TestProcessPartition(IronicTestAbstract): + def setUp(self): + super(TestProcessPartition, self).setUp() self.driver._partition_data = self.mock_part_data = mock.MagicMock() self.driver._add_partition = self.mock_add_part = mock.MagicMock() self.mock_add_part.return_value = self.mock_part = mock.MagicMock() diff --git a/bareon/tests/test_ironic_ks_spaces_validator.py b/bareon/tests/test_ironic_data_validator.py similarity index 60% rename from bareon/tests/test_ironic_ks_spaces_validator.py rename to bareon/tests/test_ironic_data_validator.py index d18c81a..1f9836b 100644 --- a/bareon/tests/test_ironic_ks_spaces_validator.py +++ b/bareon/tests/test_ironic_data_validator.py @@ -16,10 +16,10 @@ import copy import unittest2 -from bareon.drivers.data import ks_spaces_validator as kssv +from bareon.drivers.data import ironic from bareon import errors -SAMPLE_SCHEME = [ +SAMPLE_CHUNK_PARTITIONS = [ { "id": { "type": "name", @@ -175,67 +175,108 @@ SAMPLE_SCHEME = [ } ] +SAMPLE_PAYLOAD = { + 'partitions': SAMPLE_CHUNK_PARTITIONS, + 'images': [] +} -class TestKSSpacesValidator(unittest2.TestCase): + +class TestIronicDataValidator(unittest2.TestCase): def setUp(self): - super(TestKSSpacesValidator, self).setUp() - self.fake_scheme = copy.deepcopy(SAMPLE_SCHEME) + super(TestIronicDataValidator, self).setUp() + self.payload = copy.deepcopy(SAMPLE_PAYLOAD) - def test_validate_ok(self): - kssv.validate(self.fake_scheme, 'ironic') + def test_no_error(self): + ironic.Ironic.validate_data(self.payload) - def test_validate_jsoschema_fail(self): - self.assertRaises(errors.WrongPartitionSchemeError, - kssv.validate, [{}], 'ironic') + def test_fail(self): + self.assertRaises( + errors.WrongInputDataError, ironic.Ironic.validate_data, [{}]) - def test_validate_no_disks_fail(self): - self.assertRaises(errors.WrongPartitionSchemeError, - kssv.validate, self.fake_scheme[-2:], 'ironic') + def test_required_fields(self): + for field in ['partitions']: + payload = copy.deepcopy(self.payload) + del payload[field] + self.assertRaises( + errors.WrongInputDataError, ironic.Ironic.validate_data, + payload) - def test_validate_16T_root_volume_fail(self): - self.fake_scheme[3]['volumes'][0]['size'] = 16777216 + 1 - self.assertRaises(errors.WrongPartitionSchemeError, - kssv.validate, self.fake_scheme, 'ironic') + def test_disks_no_disks_fail(self): + partitions = self.payload['partitions'] + partitions[:-2] = [] + self.assertRaises( + errors.WrongInputDataError, ironic.Ironic.validate_data, + self.payload) - def test_validate_volume_type_fail(self): + @unittest2.skip( + 'FIXME(dbogun): Invalid test - failed because invalid data ' + 'type(expect sting in size field) but not because illegal partition ' + 'size') + def test_disks_16T_root_volume_fail(self): + partitions = self.payload['partitions'] + partitions[3]['volumes'][0]['size'] = 16777216 + 1 + self.assertRaises( + errors.WrongInputDataError, ironic.Ironic.validate_data, + self.payload) + + def test_disks_volume_type_fail(self): incorrect_values_for_type = [ False, True, 0, 1, None, object ] + partitions = self.payload['partitions'] for value in incorrect_values_for_type: - self.fake_scheme[0]['volumes'][1]['type'] = value - self.assertRaises(errors.WrongPartitionSchemeError, - kssv.validate, self.fake_scheme, 'ironic') + partitions[0]['volumes'][1]['type'] = value + self.assertRaises( + errors.WrongInputDataError, ironic.Ironic.validate_data, + self.payload) - def test_validate_volume_size_fail(self): + def test_disks_volume_size_fail(self): incorrect_values_for_size = [ False, True, 0, 1, None, object ] + partitions = self.payload['partitions'] for value in incorrect_values_for_size: - self.fake_scheme[0]['volumes'][1]['size'] = value - self.assertRaises(errors.WrongPartitionSchemeError, - kssv.validate, self.fake_scheme, 'ironic') + partitions[0]['volumes'][1]['size'] = value + self.assertRaises( + errors.WrongInputDataError, ironic.Ironic.validate_data, + self.payload) - def test_validate_device_id_fail(self): + def test_disks_device_id_fail(self): incorrect_values_for_id = [ False, True, 0, 1, None, object ] + partitions = self.payload['partitions'] for value in incorrect_values_for_id: - self.fake_scheme[0]['id'] = value - self.assertRaises(errors.WrongPartitionSchemeError, - kssv.validate, self.fake_scheme, 'ironic') + partitions[0]['id'] = value + self.assertRaises( + errors.WrongInputDataError, ironic.Ironic.validate_data, + self.payload) - def test_validate_missed_property(self): + def test_disks_missed_property_fail(self): required = ['id', 'size', 'volumes', 'type'] for prop in required: - fake = copy.deepcopy(self.fake_scheme) - del fake[0][prop] - self.assertRaises(errors.WrongPartitionSchemeError, - kssv.validate, fake, 'ironic') + fake = copy.deepcopy(self.payload) + partitions = fake['partitions'] + del partitions[0][prop] + self.assertRaises( + errors.WrongInputDataError, ironic.Ironic.validate_data, fake) def test_validate_missed_volume_property(self): required = ['type', 'size', 'vg'] for prop in required: - fake = copy.deepcopy(self.fake_scheme) - del fake[0]['volumes'][3][prop] - self.assertRaises(errors.WrongPartitionSchemeError, - kssv.validate, fake, 'ironic') + fake = copy.deepcopy(self.payload) + partitions = fake['partitions'] + del partitions[0]['volumes'][3][prop] + self.assertRaises( + errors.WrongInputDataError, ironic.Ironic.validate_data, fake) + + def test_disks_keep_data_flag_type(self): + partitions = self.payload['partitions'] + partitions[0]['volumes'][1]['keep_data'] = "True" + self.assertRaises( + errors.WrongInputDataError, ironic.Ironic.validate_data, + self.payload) + + @staticmethod + def _get_disks(payload): + return payload['partitions'] diff --git a/bareon/tests/test_ks_spaces_validator.py b/bareon/tests/test_ks_spaces_validator.py deleted file mode 100644 index 2ce533f..0000000 --- a/bareon/tests/test_ks_spaces_validator.py +++ /dev/null @@ -1,205 +0,0 @@ -# Copyright 2014 Mirantis, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import copy - -import unittest2 - -from bareon.drivers.data import ks_spaces_validator as kssv -from bareon import errors - -SAMPLE_SCHEME = [ - { - "name": "sda", - "extra": [ - "disk/by-id/scsi-SATA_VBOX_HARDDISK_VB69050467-b385c7cd", - "disk/by-id/ata-VBOX_HARDDISK_VB69050467-b385c7cd" - ], - "free_space": 64907, - "volumes": [ - { - "type": "boot", - "size": 300 - }, - { - "mount": "/boot", - "size": 200, - "type": "raid", - "file_system": "ext2", - "name": "Boot" - }, - { - "type": "lvm_meta_pool", - "size": 0 - }, - { - "size": 19438, - "type": "pv", - "lvm_meta_size": 64, - "vg": "os" - }, - { - "size": 45597, - "type": "pv", - "lvm_meta_size": 64, - "vg": "image" - } - ], - "type": "disk", - "id": "sda", - "size": 65535 - }, - { - "name": "sdb", - "extra": [ - "disk/by-id/scsi-SATA_VBOX_HARDDISK_VBf2923215-708af674", - "disk/by-id/ata-VBOX_HARDDISK_VBf2923215-708af674" - ], - "free_space": 64907, - "volumes": [ - { - "type": "boot", - "size": 300 - }, - { - "mount": "/boot", - "size": 200, - "type": "raid", - "file_system": "ext2", - "name": "Boot" - }, - { - "type": "lvm_meta_pool", - "size": 64 - }, - { - "size": 0, - "type": "pv", - "lvm_meta_size": 0, - "vg": "os" - }, - { - "size": 64971, - "type": "pv", - "lvm_meta_size": 64, - "vg": "image" - } - ], - "type": "disk", - "id": "sdb", - "size": 65535 - }, - { - "name": "sdc", - "extra": [ - "disk/by-id/scsi-SATA_VBOX_HARDDISK_VB50ee61eb-84e74fdf", - "disk/by-id/ata-VBOX_HARDDISK_VB50ee61eb-84e74fdf" - ], - "free_space": 64907, - "volumes": [ - { - "type": "boot", - "size": 300 - }, - { - "mount": "/boot", - "size": 200, - "type": "raid", - "file_system": "ext2", - "name": "Boot" - }, - { - "type": "lvm_meta_pool", - "size": 64 - }, - { - "size": 0, - "type": "pv", - "lvm_meta_size": 0, - "vg": "os" - }, - { - "size": 64971, - "type": "pv", - "lvm_meta_size": 64, - "vg": "image" - } - ], - "type": "disk", - "id": "disk/by-path/pci-0000:00:0d.0-scsi-0:0:0:0", - "size": 65535 - }, - { - "_allocate_size": "min", - "label": "Base System", - "min_size": 19374, - "volumes": [ - { - "mount": "/", - "size": 15360, - "type": "lv", - "name": "root", - "file_system": "ext4" - }, - { - "mount": "swap", - "size": 4014, - "type": "lv", - "name": "swap", - "file_system": "swap" - } - ], - "type": "vg", - "id": "os" - }, - { - "_allocate_size": "all", - "label": "Image Storage", - "min_size": 5120, - "volumes": [ - { - "mount": "/var/lib/glance", - "size": 175347, - "type": "lv", - "name": "glance", - "file_system": "xfs" - } - ], - "type": "vg", - "id": "image" - } -] - - -class TestKSSpacesValidator(unittest2.TestCase): - def setUp(self): - super(TestKSSpacesValidator, self).setUp() - self.fake_scheme = copy.deepcopy(SAMPLE_SCHEME) - - def test_validate_ok(self): - kssv.validate(self.fake_scheme) - - def test_validate_jsoschema_fail(self): - self.assertRaises(errors.WrongPartitionSchemeError, kssv.validate, - [{}]) - - def test_validate_no_disks_fail(self): - self.assertRaises(errors.WrongPartitionSchemeError, kssv.validate, - self.fake_scheme[-2:]) - - @unittest2.skip("Fix after cray rebase") - def test_validate_16T_root_volume_fail(self): - self.fake_scheme[3]['volumes'][0]['size'] = 16777216 + 1 - self.assertRaises(errors.WrongPartitionSchemeError, kssv.validate, - self.fake_scheme) diff --git a/bareon/tests/test_nailgun_ks_spaces_validator.py b/bareon/tests/test_nailgun_data_validator.py similarity index 68% rename from bareon/tests/test_nailgun_ks_spaces_validator.py rename to bareon/tests/test_nailgun_data_validator.py index c7a8a6a..ab39ac5 100644 --- a/bareon/tests/test_nailgun_ks_spaces_validator.py +++ b/bareon/tests/test_nailgun_data_validator.py @@ -16,10 +16,10 @@ import copy import unittest2 -from bareon.drivers.data import ks_spaces_validator as kssv +from bareon.drivers.data import nailgun from bareon import errors -SAMPLE_SCHEME = [ +SAMPLE_CHUNK_SPACES = [ { "name": "sda", "extra": [ @@ -181,71 +181,96 @@ SAMPLE_SCHEME = [ } ] +SAMPLE_PAYLOAD = { + 'ks_meta': { + 'pm_data': { + 'ks_spaces': SAMPLE_CHUNK_SPACES + } + } +} -class TestKSSpacesValidator(unittest2.TestCase): + +class TestNailgunDataValidator(unittest2.TestCase): def setUp(self): - super(TestKSSpacesValidator, self).setUp() - self.fake_scheme = copy.deepcopy(SAMPLE_SCHEME) + super(TestNailgunDataValidator, self).setUp() + self.payload = copy.deepcopy(SAMPLE_PAYLOAD) + self.payload_spaces = self._get_spaces(self.payload) - def test_validate_ok(self): - kssv.validate(self.fake_scheme, 'nailgun') + def test_no_error(self): + nailgun.Nailgun.validate_data(self.payload) - def test_validate_jsoschema_fail(self): - self.assertRaises(errors.WrongPartitionSchemeError, - kssv.validate, [{}], 'nailgun') + def test_fail(self): + self.assertRaises( + errors.WrongInputDataError, nailgun.Nailgun.validate_data, {}) - def test_validate_no_disks_fail(self): - self.assertRaises(errors.WrongPartitionSchemeError, - kssv.validate, self.fake_scheme[-2:], 'nailgun') + def test_disks_no_disks_fail(self): + self.payload_spaces[:-2] = [] + self.assertRaises( + errors.WrongInputDataError, nailgun.Nailgun.validate_data, + self.payload) - def test_validate_free_space_type_fail(self): + def test_disks_free_space_type_fail(self): incorrect_values_for_free_space = [ False, True, '0', '1', None, object ] for value in incorrect_values_for_free_space: - self.fake_scheme[0]['free_space'] = value - self.assertRaises(errors.WrongPartitionSchemeError, - kssv.validate, self.fake_scheme, 'nailgun') + self.payload_spaces[0]['free_space'] = value + self.assertRaises( + errors.WrongInputDataError, nailgun.Nailgun.validate_data, + self.payload) - def test_validate_volume_type_fail(self): + def test_disks_volume_type_fail(self): incorrect_values_for_type = [ False, True, 0, 1, None, object ] for value in incorrect_values_for_type: - self.fake_scheme[0]['volumes'][1]['type'] = value - self.assertRaises(errors.WrongPartitionSchemeError, - kssv.validate, self.fake_scheme, 'nailgun') + self.payload_spaces[0]['volumes'][1]['type'] = value + self.assertRaises( + errors.WrongInputDataError, nailgun.Nailgun.validate_data, + self.payload) - def test_validate_volume_size_fail(self): + def test_disks_volume_size_fail(self): incorrect_values_for_size = [ False, True, '0', '1', None, object ] for value in incorrect_values_for_size: - self.fake_scheme[0]['volumes'][1]['size'] = value - self.assertRaises(errors.WrongPartitionSchemeError, - kssv.validate, self.fake_scheme, 'nailgun') + self.payload_spaces[0]['volumes'][1]['size'] = value + self.assertRaises( + errors.WrongInputDataError, nailgun.Nailgun.validate_data, + self.payload) - def test_validate_device_id_fail(self): + def test_disks_device_id_fail(self): incorrect_values_for_id = [ False, True, 0, 1, None, object ] for value in incorrect_values_for_id: - self.fake_scheme[0]['id'] = value - self.assertRaises(errors.WrongPartitionSchemeError, - kssv.validate, self.fake_scheme, 'nailgun') + self.payload_spaces[0]['id'] = value + self.assertRaises( + errors.WrongInputDataError, nailgun.Nailgun.validate_data, + self.payload) - def test_validate_missed_property(self): + def test_disks_missed_property(self): required = ['id', 'size', 'volumes', 'type', 'free_space', 'volumes'] for prop in required: - fake = copy.deepcopy(self.fake_scheme) - del fake[0][prop] - self.assertRaises(errors.WrongPartitionSchemeError, - kssv.validate, fake, 'nailgun') + payload = copy.deepcopy(self.payload) + spaces = self._get_spaces(payload) + del spaces[0][prop] + self.assertRaises( + errors.WrongInputDataError, nailgun.Nailgun.validate_data, + payload) - def test_validate_missed_volume_property(self): + def test_disks_missed_volume_property(self): required = ['type', 'size', 'vg'] for prop in required: - fake = copy.deepcopy(self.fake_scheme) - del fake[0]['volumes'][3][prop] - self.assertRaises(errors.WrongPartitionSchemeError, - kssv.validate, fake, 'nailgun') + payload = copy.deepcopy(self.payload) + spaces = self._get_spaces(payload) + del spaces[0]['volumes'][3][prop] + self.assertRaises( + errors.WrongInputDataError, nailgun.Nailgun.validate_data, + payload) + + @staticmethod + def _get_spaces(payload): + payload = payload['ks_meta'] + payload = payload['pm_data'] + return payload['ks_spaces'] diff --git a/bareon/tests/test_simple_nailgun_driver.py b/bareon/tests/test_simple_nailgun_driver.py index e07e3c2..0acaa2f 100644 --- a/bareon/tests/test_simple_nailgun_driver.py +++ b/bareon/tests/test_simple_nailgun_driver.py @@ -30,25 +30,23 @@ from bareon.tests import base parse_configdrive_scheme=lambda x: objects.ConfigDriveScheme(), parse_image_scheme=lambda x: objects.ImageScheme()) class TestObjectDeserialization(unittest2.TestCase): - def test_driver_always_has_correct_objects(self): - driver = simple.NailgunSimpleDriver({}) + driver = simple.NailgunSimpleDriver(self._minimal_payload()) assert isinstance(driver.partition_scheme, objects.PartitionScheme) def test_lv_data_is_loaded(self): - lv_data = { - 'partitioning': { - 'lvs': [ - { - 'name': 'lv-name', - 'size': 12345, - 'vgname': 'vg-name', - }, - ] - } + payload = self._minimal_payload() + payload['partitioning'] = { + 'lvs': [ + { + 'name': 'lv-name', + 'size': 12345, + 'vgname': 'vg-name', + }, + ] } - driver = simple.NailgunSimpleDriver(lv_data) + driver = simple.NailgunSimpleDriver(payload) lv = driver.partition_scheme.lvs[0] assert len(driver.partition_scheme.lvs) == 1 assert isinstance(lv, objects.LV) @@ -57,19 +55,18 @@ class TestObjectDeserialization(unittest2.TestCase): assert lv.vgname == 'vg-name' def test_pv_data_is_loaded(self): - pv_data = { - 'partitioning': { - 'pvs': [ - { - 'metadatacopies': 2, - 'metadatasize': 28, - 'name': '/dev/sda5' - }, - ] - } + payload = self._minimal_payload() + payload['partitioning'] = { + 'pvs': [ + { + 'metadatacopies': 2, + 'metadatasize': 28, + 'name': '/dev/sda5' + }, + ] } - driver = simple.NailgunSimpleDriver(pv_data) + driver = simple.NailgunSimpleDriver(payload) pv = driver.partition_scheme.pvs[0] assert len(driver.partition_scheme.pvs) == 1 assert isinstance(pv, objects.PV) @@ -78,22 +75,21 @@ class TestObjectDeserialization(unittest2.TestCase): assert pv.metadatasize == 28 def test_vg_data_is_loaded(self): - vg_data = { - 'partitioning': { - 'vgs': [ - { - 'name': 'image', - 'pvnames': [ - '/dev/sda6', - '/dev/sdb3', - '/dev/sdc3', - ] - }, - ] - } + payload = self._minimal_payload() + payload['partitioning'] = { + 'vgs': [ + { + 'name': 'image', + 'pvnames': [ + '/dev/sda6', + '/dev/sdb3', + '/dev/sdc3', + ] + }, + ] } - driver = simple.NailgunSimpleDriver(vg_data) + driver = simple.NailgunSimpleDriver(payload) vg = driver.partition_scheme.vgs[0] assert len(driver.partition_scheme.vgs) == 1 assert isinstance(vg, objects.VG) @@ -108,21 +104,20 @@ class TestObjectDeserialization(unittest2.TestCase): ) def test_fs_data_is_loaded(self): - fs_data = { - 'partitioning': { - 'fss': [ - { - 'device': '/dev/sda3', - 'fs_label': 'some-label', - 'fs_options': 'some-options', - 'fs_type': 'ext2', - 'mount': '/boot' - }, - ] - } + payload = self._minimal_payload() + payload['partitioning'] = { + 'fss': [ + { + 'device': '/dev/sda3', + 'fs_label': 'some-label', + 'fs_options': 'some-options', + 'fs_type': 'ext2', + 'mount': '/boot' + }, + ] } - driver = simple.NailgunSimpleDriver(fs_data) + driver = simple.NailgunSimpleDriver(payload) fs = driver.partition_scheme.fss[0] assert len(driver.partition_scheme.fss) == 1 assert isinstance(fs, objects.FS) @@ -133,34 +128,33 @@ class TestObjectDeserialization(unittest2.TestCase): assert fs.mount == '/boot' def test_parted_data_is_loaded(self): - parted_data = { - 'partitioning': { - 'parteds': [ - { - 'label': 'gpt', - 'name': '/dev/sdb', - 'partitions': [ - { - 'begin': 1, - 'configdrive': False, - 'count': 1, - 'device': '/dev/sdb', - 'end': 25, - 'flags': [ - 'bios_grub', - 'xyz', - ], - 'guid': None, - 'name': '/dev/sdb1', - 'partition_type': 'primary' - }, - ] - }, - ] - } + payload = self._minimal_payload() + payload['partitioning'] = { + 'parteds': [ + { + 'label': 'gpt', + 'name': '/dev/sdb', + 'partitions': [ + { + 'begin': 1, + 'configdrive': False, + 'count': 1, + 'device': '/dev/sdb', + 'end': 25, + 'flags': [ + 'bios_grub', + 'xyz', + ], + 'guid': None, + 'name': '/dev/sdb1', + 'partition_type': 'primary' + }, + ] + }, + ] } - driver = simple.NailgunSimpleDriver(parted_data) + driver = simple.NailgunSimpleDriver(payload) parted = driver.partition_scheme.parteds[0] partition = parted.partitions[0] assert len(driver.partition_scheme.parteds) == 1 @@ -179,26 +173,25 @@ class TestObjectDeserialization(unittest2.TestCase): assert partition.type == 'primary' def test_md_data_is_loaded(self): - md_data = { - 'partitioning': { - 'mds': [ - { - 'name': 'some-raid', - 'level': 1, - 'devices': [ - '/dev/sda', - '/dev/sdc', - ], - 'spares': [ - '/dev/sdb', - '/dev/sdd', - ] - }, - ] - } + payload = self._minimal_payload() + payload['partitioning'] = { + 'mds': [ + { + 'name': 'some-raid', + 'level': 1, + 'devices': [ + '/dev/sda', + '/dev/sdc', + ], + 'spares': [ + '/dev/sdb', + '/dev/sdd', + ] + }, + ] } - driver = simple.NailgunSimpleDriver(md_data) + driver = simple.NailgunSimpleDriver(payload) md = driver.partition_scheme.mds[0] assert len(driver.partition_scheme.mds) == 1 assert isinstance(md, objects.MD) @@ -207,6 +200,26 @@ class TestObjectDeserialization(unittest2.TestCase): self.assertItemsEqual(md.devices, ['/dev/sda', '/dev/sdc']) self.assertItemsEqual(md.spares, ['/dev/sdb', '/dev/sdd']) + @staticmethod + def _minimal_payload(): + return { + 'ks_meta': { + 'pm_data': { + 'ks_spaces': [ + { + 'type': 'disk', + 'id': 'dummy-disk-id', + 'name': 'dummy0', + 'free_space': 0, + 'size': 128, + 'extra': [], + 'volumes': [] + } + ] + } + } + } + @requests_mock.mock() class TestFullDataRead(unittest2.TestCase): diff --git a/bareon/utils/partition.py b/bareon/utils/partition.py index 0b5672d..f63b1d5 100644 --- a/bareon/utils/partition.py +++ b/bareon/utils/partition.py @@ -22,6 +22,11 @@ from bareon.utils import utils LOG = logging.getLogger(__name__) PARTITION_ALIGMENT = ('none', 'cylinder', 'minimal', 'optimal') +KiB = 1024 +MiB = KiB * 1024 +GiB = MiB * 1024 +TiB = GiB * 1024 + def parse_partition_info(parted_output): lines = parted_output.split('\n') diff --git a/setup.cfg b/setup.cfg index 177edbb..4258d34 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = bareon -version = 0.0.1.a3 +version = 0.0.2 author = Mirantis author-email = openstack-dev@lists.openstack.org summary = Bareon