Add simple data driver for partitioning info
This new "simple" data driver takes serialized partitioning info that is provided before provisioning from external tool. It does not make any calculations for that data and expect it to have all required informations about partitioning. Other changes: * added unittest2 to use some of it features * added requests_mock to mock http requests * added objects conversion to/from dictionary to make serialization easier * added a common "interface" for data drivers * fixed test for "do_build_image" - now a correct data driver is used Change-Id: I673cde6f0ead9945919a87cd1cfce7ed09c6e593 Implements: blueprint volume-manager-refactoring
This commit is contained in:
parent
ce4bfd0037
commit
9e22866f3e
@ -28,3 +28,23 @@ class BaseDataDriver(object):
|
||||
|
||||
def __init__(self, data):
|
||||
self.data = copy.deepcopy(data)
|
||||
|
||||
@abc.abstractproperty
|
||||
def partition_scheme(self):
|
||||
"""Retruns instance of PartionScheme object"""
|
||||
|
||||
@abc.abstractproperty
|
||||
def image_scheme(self):
|
||||
"""Returns instance of ImageScheme object"""
|
||||
|
||||
@abc.abstractproperty
|
||||
def grub(self):
|
||||
"""Returns instance of Grub object"""
|
||||
|
||||
@abc.abstractproperty
|
||||
def operating_system(self):
|
||||
"""Returns instance of OperatingSystem object"""
|
||||
|
||||
@abc.abstractproperty
|
||||
def configdrive_scheme(self):
|
||||
"""Returns instance of ConfigDriveScheme object"""
|
||||
|
@ -15,12 +15,12 @@
|
||||
import itertools
|
||||
import math
|
||||
import os
|
||||
import six
|
||||
import yaml
|
||||
|
||||
import six
|
||||
from six.moves.urllib.parse import urljoin
|
||||
from six.moves.urllib.parse import urlparse
|
||||
from six.moves.urllib.parse import urlsplit
|
||||
import yaml
|
||||
|
||||
from fuel_agent.drivers.base import BaseDataDriver
|
||||
from fuel_agent.drivers import ks_spaces_validator
|
||||
@ -67,6 +67,8 @@ def match_device(hu_disk, ks_disk):
|
||||
|
||||
|
||||
class Nailgun(BaseDataDriver):
|
||||
"""Driver for parsing regular volumes metadata from Nailgun."""
|
||||
|
||||
def __init__(self, data):
|
||||
super(Nailgun, self).__init__(data)
|
||||
|
||||
@ -79,11 +81,31 @@ class Nailgun(BaseDataDriver):
|
||||
# get rid of md over all disks for /boot partition.
|
||||
self._boot_done = False
|
||||
|
||||
self.partition_scheme = self.parse_partition_scheme()
|
||||
self.grub = self.parse_grub()
|
||||
self.configdrive_scheme = self.parse_configdrive_scheme()
|
||||
self._partition_scheme = self.parse_partition_scheme()
|
||||
self._grub = self.parse_grub()
|
||||
self._configdrive_scheme = self.parse_configdrive_scheme()
|
||||
# parsing image scheme needs partition scheme has been parsed
|
||||
self.image_scheme = self.parse_image_scheme()
|
||||
self._image_scheme = self.parse_image_scheme()
|
||||
|
||||
@property
|
||||
def partition_scheme(self):
|
||||
return self._partition_scheme
|
||||
|
||||
@property
|
||||
def image_scheme(self):
|
||||
return self._image_scheme
|
||||
|
||||
@property
|
||||
def grub(self):
|
||||
return self._grub
|
||||
|
||||
@property
|
||||
def operating_system(self):
|
||||
return None
|
||||
|
||||
@property
|
||||
def configdrive_scheme(self):
|
||||
return self._configdrive_scheme
|
||||
|
||||
def partition_data(self):
|
||||
return self.data['ks_meta']['pm_data']['ks_spaces']
|
||||
@ -346,7 +368,7 @@ class Nailgun(BaseDataDriver):
|
||||
for volume in vg['volumes']:
|
||||
LOG.debug('Processing lv %s' % volume['name'])
|
||||
if volume['size'] <= 0:
|
||||
LOG.debug('Lv size is zero. Skipping.')
|
||||
LOG.debug('LogicalVolume size is zero. Skipping.')
|
||||
continue
|
||||
|
||||
if volume['type'] == 'lv':
|
||||
@ -543,8 +565,31 @@ class NailgunBuildImage(BaseDataDriver):
|
||||
|
||||
def __init__(self, data):
|
||||
super(NailgunBuildImage, self).__init__(data)
|
||||
self._image_scheme = objects.ImageScheme()
|
||||
self._partition_scheme = objects.PartitionScheme()
|
||||
|
||||
self.parse_schemes()
|
||||
self.parse_operating_system()
|
||||
self._operating_system = self.parse_operating_system()
|
||||
|
||||
@property
|
||||
def partition_scheme(self):
|
||||
return self._partition_scheme
|
||||
|
||||
@property
|
||||
def image_scheme(self):
|
||||
return self._image_scheme
|
||||
|
||||
@property
|
||||
def grub(self):
|
||||
return None
|
||||
|
||||
@property
|
||||
def operating_system(self):
|
||||
return self._operating_system
|
||||
|
||||
@property
|
||||
def configdrive_scheme(self):
|
||||
return None
|
||||
|
||||
def parse_operating_system(self):
|
||||
if self.data.get('codename').lower() != 'trusty':
|
||||
@ -563,11 +608,9 @@ class NailgunBuildImage(BaseDataDriver):
|
||||
section=repo['section'],
|
||||
priority=repo['priority']))
|
||||
|
||||
self.operating_system = objects.Ubuntu(repos=repos, packages=packages)
|
||||
return objects.Ubuntu(repos=repos, packages=packages)
|
||||
|
||||
def parse_schemes(self):
|
||||
self.image_scheme = objects.ImageScheme()
|
||||
self.partition_scheme = objects.PartitionScheme()
|
||||
|
||||
for mount, image in six.iteritems(self.data['image_data']):
|
||||
filename = os.path.basename(urlsplit(image['uri']).path)
|
||||
@ -575,13 +618,13 @@ class NailgunBuildImage(BaseDataDriver):
|
||||
# during initialization.
|
||||
device = objects.Loop()
|
||||
|
||||
self.image_scheme.add_image(
|
||||
self._image_scheme.add_image(
|
||||
uri='file://' + os.path.join(self.data['output'], filename),
|
||||
format=image['format'],
|
||||
container=image['container'],
|
||||
target_device=device)
|
||||
|
||||
self.partition_scheme.add_fs(
|
||||
self._partition_scheme.add_fs(
|
||||
device=device,
|
||||
mount=mount,
|
||||
fs_type=image['format'])
|
||||
|
67
fuel_agent/drivers/simple.py
Normal file
67
fuel_agent/drivers/simple.py
Normal file
@ -0,0 +1,67 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright 2015 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.
|
||||
|
||||
from fuel_agent.drivers import nailgun
|
||||
from fuel_agent import objects
|
||||
|
||||
|
||||
class NailgunSimpleDriver(nailgun.Nailgun):
|
||||
"""Simple driver that do not make any computations.
|
||||
|
||||
This driver digest information that already has all required
|
||||
information how to perform partitioning.
|
||||
Service that sends data to fuel_agent is responsible for preparing
|
||||
it in correct format.
|
||||
"""
|
||||
|
||||
@property
|
||||
def partition_data(self):
|
||||
return self.data.get('partitioning', {})
|
||||
|
||||
@classmethod
|
||||
def parse_lv_data(cls, raw_lvs):
|
||||
return [objects.LV.from_dict(lv) for lv in raw_lvs]
|
||||
|
||||
@classmethod
|
||||
def parse_pv_data(cls, raw_pvs):
|
||||
return [objects.PV.from_dict(pv) for pv in raw_pvs]
|
||||
|
||||
@classmethod
|
||||
def parse_fs_data(cls, raw_fss):
|
||||
return [objects.FS.from_dict(fs) for fs in raw_fss]
|
||||
|
||||
@classmethod
|
||||
def parse_vg_data(cls, raw_vgs):
|
||||
return [objects.VG.from_dict(vg) for vg in raw_vgs]
|
||||
|
||||
@classmethod
|
||||
def parse_md_data(cls, raw_mds):
|
||||
return [objects.MD.from_dict(md) for md in raw_mds]
|
||||
|
||||
@classmethod
|
||||
def parse_parted_data(cls, raw_parteds):
|
||||
return [objects.Parted.from_dict(parted) for parted in raw_parteds]
|
||||
|
||||
def parse_partition_scheme(self):
|
||||
partition_scheme = objects.PartitionScheme()
|
||||
|
||||
for obj in ('lv', 'pv', 'fs', 'vg', 'md', 'parted'):
|
||||
attr = '{0}s'.format(obj)
|
||||
parse_method = getattr(self, 'parse_{0}_data'.format(obj))
|
||||
raw = self.partition_data.get(attr, {})
|
||||
setattr(partition_scheme, attr, parse_method(raw))
|
||||
|
||||
return partition_scheme
|
@ -22,18 +22,19 @@ from fuel_agent.objects.image import Image
|
||||
from fuel_agent.objects.image import ImageScheme
|
||||
from fuel_agent.objects.operating_system import OperatingSystem
|
||||
from fuel_agent.objects.operating_system import Ubuntu
|
||||
from fuel_agent.objects.partition import Fs
|
||||
from fuel_agent.objects.partition import Lv
|
||||
from fuel_agent.objects.partition import Md
|
||||
from fuel_agent.objects.partition import FS
|
||||
from fuel_agent.objects.partition import LV
|
||||
from fuel_agent.objects.partition import MD
|
||||
from fuel_agent.objects.partition import Parted
|
||||
from fuel_agent.objects.partition import Partition
|
||||
from fuel_agent.objects.partition import PartitionScheme
|
||||
from fuel_agent.objects.partition import Pv
|
||||
from fuel_agent.objects.partition import Vg
|
||||
from fuel_agent.objects.partition import PV
|
||||
from fuel_agent.objects.partition import VG
|
||||
from fuel_agent.objects.repo import DEBRepo
|
||||
from fuel_agent.objects.repo import Repo
|
||||
|
||||
__all__ = [
|
||||
'Partition', 'Pv', 'Vg', 'Lv', 'Md', 'Fs', 'PartitionScheme',
|
||||
'Partition', 'Parted', 'PV', 'VG', 'LV', 'MD', 'FS', 'PartitionScheme',
|
||||
'ConfigDriveCommon', 'ConfigDrivePuppet', 'ConfigDriveMcollective',
|
||||
'ConfigDriveScheme', 'Image', 'ImageScheme', 'Grub',
|
||||
'OperatingSystem', 'Ubuntu',
|
||||
|
32
fuel_agent/objects/base.py
Normal file
32
fuel_agent/objects/base.py
Normal file
@ -0,0 +1,32 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright 2015 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 abc
|
||||
import six
|
||||
|
||||
from fuel_agent.utils.decorators import abstractclassmethod
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class Serializable(object):
|
||||
|
||||
@abc.abstractmethod
|
||||
def to_dict(self):
|
||||
pass
|
||||
|
||||
@abstractclassmethod
|
||||
def from_dict(cls, data):
|
||||
pass
|
@ -12,20 +12,23 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import copy
|
||||
import os
|
||||
|
||||
from fuel_agent import errors
|
||||
from fuel_agent.objects import base
|
||||
from fuel_agent.openstack.common import log as logging
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Parted(object):
|
||||
def __init__(self, name, label):
|
||||
class Parted(base.Serializable):
|
||||
|
||||
def __init__(self, name, label, partitions=None, install_bootloader=False):
|
||||
self.name = name
|
||||
self.label = label
|
||||
self.partitions = []
|
||||
self.install_bootloader = False
|
||||
self.partitions = partitions or []
|
||||
self.install_bootloader = install_bootloader
|
||||
|
||||
def add_partition(self, **kwargs):
|
||||
# TODO(kozhukalov): validate before appending
|
||||
@ -99,14 +102,31 @@ class Parted(object):
|
||||
separator = 'p'
|
||||
return '%s%s%s' % (self.name, separator, self.next_count())
|
||||
|
||||
def to_dict(self):
|
||||
partitions = [partition.to_dict() for partition in self.partitions]
|
||||
return {
|
||||
'name': self.name,
|
||||
'label': self.label,
|
||||
'partitions': partitions,
|
||||
'install_bootloader': self.install_bootloader,
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, data):
|
||||
data = copy.deepcopy(data)
|
||||
raw_partitions = data.pop('partitions')
|
||||
partitions = [Partition.from_dict(partition)
|
||||
for partition in raw_partitions]
|
||||
return cls(partitions=partitions, **data)
|
||||
|
||||
|
||||
class Partition(base.Serializable):
|
||||
|
||||
class Partition(object):
|
||||
def __init__(self, name, count, device, begin, end, partition_type,
|
||||
flags=None, guid=None, configdrive=False):
|
||||
self.name = name
|
||||
self.count = count
|
||||
self.device = device
|
||||
self.name = name
|
||||
self.begin = begin
|
||||
self.end = end
|
||||
self.type = partition_type
|
||||
@ -121,15 +141,48 @@ class Partition(object):
|
||||
def set_guid(self, guid):
|
||||
self.guid = guid
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
'name': self.name,
|
||||
'count': self.count,
|
||||
'device': self.device,
|
||||
'begin': self.begin,
|
||||
'end': self.end,
|
||||
'partition_type': self.type,
|
||||
'flags': self.flags,
|
||||
'guid': self.guid,
|
||||
'configdrive': self.configdrive,
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, data):
|
||||
return cls(**data)
|
||||
|
||||
|
||||
class PhysicalVolume(base.Serializable):
|
||||
|
||||
class Pv(object):
|
||||
def __init__(self, name, metadatasize=16, metadatacopies=2):
|
||||
self.name = name
|
||||
self.metadatasize = metadatasize
|
||||
self.metadatacopies = metadatacopies
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
'name': self.name,
|
||||
'metadatasize': self.metadatasize,
|
||||
'metadatacopies': self.metadatacopies,
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, data):
|
||||
return cls(**data)
|
||||
|
||||
|
||||
PV = PhysicalVolume
|
||||
|
||||
|
||||
class VolumeGroup(base.Serializable):
|
||||
|
||||
class Vg(object):
|
||||
def __init__(self, name, pvnames=None):
|
||||
self.name = name
|
||||
self.pvnames = pvnames or []
|
||||
@ -138,8 +191,22 @@ class Vg(object):
|
||||
if pvname not in self.pvnames:
|
||||
self.pvnames.append(pvname)
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
'name': self.name,
|
||||
'pvnames': self.pvnames
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, data):
|
||||
return cls(**data)
|
||||
|
||||
|
||||
VG = VolumeGroup
|
||||
|
||||
|
||||
class LogicalVolume(base.Serializable):
|
||||
|
||||
class Lv(object):
|
||||
def __init__(self, name, vgname, size):
|
||||
self.name = name
|
||||
self.vgname = vgname
|
||||
@ -150,8 +217,23 @@ class Lv(object):
|
||||
return '/dev/mapper/%s-%s' % (self.vgname.replace('-', '--'),
|
||||
self.name.replace('-', '--'))
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
'name': self.name,
|
||||
'vgname': self.vgname,
|
||||
'size': self.size,
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, data):
|
||||
return cls(**data)
|
||||
|
||||
|
||||
LV = LogicalVolume
|
||||
|
||||
|
||||
class MultipleDevice(base.Serializable):
|
||||
|
||||
class Md(object):
|
||||
def __init__(self, name, level,
|
||||
devices=None, spares=None):
|
||||
self.name = name
|
||||
@ -173,8 +255,24 @@ class Md(object):
|
||||
'device %s is already attached' % device)
|
||||
self.spares.append(device)
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
'name': self.name,
|
||||
'level': self.level,
|
||||
'devices': self.devices,
|
||||
'spares': self.spares,
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, data):
|
||||
return cls(**data)
|
||||
|
||||
|
||||
MD = MultipleDevice
|
||||
|
||||
|
||||
class FileSystem(base.Serializable):
|
||||
|
||||
class Fs(object):
|
||||
def __init__(self, device, mount=None,
|
||||
fs_type=None, fs_options=None, fs_label=None):
|
||||
self.device = device
|
||||
@ -183,6 +281,22 @@ class Fs(object):
|
||||
self.options = fs_options or ''
|
||||
self.label = fs_label or ''
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
'device': self.device,
|
||||
'mount': self.mount,
|
||||
'fs_type': self.type,
|
||||
'fs_options': self.options,
|
||||
'fs_label': self.label,
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, data):
|
||||
return cls(**data)
|
||||
|
||||
|
||||
FS = FileSystem
|
||||
|
||||
|
||||
class PartitionScheme(object):
|
||||
def __init__(self):
|
||||
@ -199,22 +313,22 @@ class PartitionScheme(object):
|
||||
return parted
|
||||
|
||||
def add_pv(self, **kwargs):
|
||||
pv = Pv(**kwargs)
|
||||
pv = PV(**kwargs)
|
||||
self.pvs.append(pv)
|
||||
return pv
|
||||
|
||||
def add_vg(self, **kwargs):
|
||||
vg = Vg(**kwargs)
|
||||
vg = VG(**kwargs)
|
||||
self.vgs.append(vg)
|
||||
return vg
|
||||
|
||||
def add_lv(self, **kwargs):
|
||||
lv = Lv(**kwargs)
|
||||
lv = LV(**kwargs)
|
||||
self.lvs.append(lv)
|
||||
return lv
|
||||
|
||||
def add_fs(self, **kwargs):
|
||||
fs = Fs(**kwargs)
|
||||
fs = FS(**kwargs)
|
||||
self.fss.append(fs)
|
||||
return fs
|
||||
|
||||
@ -222,7 +336,7 @@ class PartitionScheme(object):
|
||||
mdkwargs = {}
|
||||
mdkwargs['name'] = kwargs.get('name') or self.md_next_name()
|
||||
mdkwargs['level'] = kwargs.get('level') or 'mirror'
|
||||
md = Md(**mdkwargs)
|
||||
md = MD(**mdkwargs)
|
||||
self.mds.append(md)
|
||||
return md
|
||||
|
||||
@ -357,3 +471,13 @@ class PartitionScheme(object):
|
||||
for prt in parted.partitions:
|
||||
if prt.configdrive:
|
||||
return prt.name
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
'parteds': [parted.to_dict() for parted in self.parteds],
|
||||
'mds': [md.to_dict() for md in self.mds],
|
||||
'pvs': [pv.to_dict() for pv in self.pvs],
|
||||
'vgs': [vg.to_dict() for vg in self.vgs],
|
||||
'lvs': [lv.to_dict() for lv in self.lvs],
|
||||
'fss': [fs.to_dict() for fs in self.fss],
|
||||
}
|
||||
|
27
fuel_agent/tests/base.py
Normal file
27
fuel_agent/tests/base.py
Normal file
@ -0,0 +1,27 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright 2015 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 os
|
||||
|
||||
|
||||
FIXTURE_PATH = os.path.join(os.path.dirname(__file__), 'fixtures')
|
||||
|
||||
|
||||
def load_fixture(filename):
|
||||
path = os.path.join(FIXTURE_PATH, filename)
|
||||
with open(path) as f:
|
||||
return json.load(f)
|
326
fuel_agent/tests/fixtures/simple_nailgun_driver.json
vendored
Normal file
326
fuel_agent/tests/fixtures/simple_nailgun_driver.json
vendored
Normal file
@ -0,0 +1,326 @@
|
||||
{
|
||||
"hostname": "node-1.domain.tld",
|
||||
"interfaces": {
|
||||
"eth0": {
|
||||
"dns_name": "node-1.domain.tld",
|
||||
"ip_address": "10.20.0.3",
|
||||
"mac_address": "08:00:27:79:da:80",
|
||||
"netmask": "255.255.255.0",
|
||||
"static": "0"
|
||||
},
|
||||
"eth1": {
|
||||
"mac_address": "08:00:27:46:43:60",
|
||||
"static": "0"
|
||||
},
|
||||
"eth2": {
|
||||
"mac_address": "08:00:27:b1:d7:15",
|
||||
"static": "0"
|
||||
}
|
||||
},
|
||||
"interfaces_extra": {
|
||||
"eth0": {
|
||||
"onboot": "yes",
|
||||
"peerdns": "no"
|
||||
},
|
||||
"eth1": {
|
||||
"onboot": "no",
|
||||
"peerdns": "no"
|
||||
},
|
||||
"eth2": {
|
||||
"onboot": "no",
|
||||
"peerdns": "no"
|
||||
}
|
||||
},
|
||||
"kernel_options": {
|
||||
"netcfg/choose_interface": "08:00:27:79:da:80",
|
||||
"udevrules": "08:00:27:79:da:80_eth0,08:00:27:46:43:60_eth1,08:00:27:b1:d7:15_eth2"
|
||||
},
|
||||
"ks_meta": {
|
||||
"auth_key": "fake_auth_key",
|
||||
"authorized_keys": [
|
||||
"fake_authorized_key1",
|
||||
"fake_authorized_key2"
|
||||
],
|
||||
"fuel_version": "5.0.1",
|
||||
"gw": "10.20.0.1",
|
||||
"image_data": {
|
||||
"/": {
|
||||
"container": "gzip",
|
||||
"format": "ext4",
|
||||
"uri": "http://fake.host.org:123/imgs/fake_image.img.gz"
|
||||
}
|
||||
},
|
||||
"install_log_2_syslog": 1,
|
||||
"master_ip": "10.20.0.2",
|
||||
"mco_auto_setup": 1,
|
||||
"mco_connector": "rabbitmq",
|
||||
"mco_enable": 1,
|
||||
"mco_host": "10.20.0.2",
|
||||
"mco_password": "marionette",
|
||||
"mco_pskey": "unset",
|
||||
"mco_user": "mcollective",
|
||||
"mco_vhost": "mcollective",
|
||||
"pm_data": {
|
||||
"kernel_params": "console=ttyS0,9600 console=tty0 rootdelay=90 nomodeset",
|
||||
"ks_spaces": []
|
||||
},
|
||||
"puppet_auto_setup": 1,
|
||||
"puppet_enable": 0,
|
||||
"puppet_master": "fuel.domain.tld",
|
||||
"repo_setup": {
|
||||
"repos": [
|
||||
{
|
||||
"name": "repo1",
|
||||
"priority": 1001,
|
||||
"section": "section",
|
||||
"suite": "suite",
|
||||
"type": "deb",
|
||||
"uri": "uri1"
|
||||
},
|
||||
{
|
||||
"name": "repo2",
|
||||
"priority": 1001,
|
||||
"section": "section",
|
||||
"suite": "suite",
|
||||
"type": "deb",
|
||||
"uri": "uri2"
|
||||
}
|
||||
]
|
||||
},
|
||||
"timezone": "America/Los_Angeles"
|
||||
},
|
||||
"name": "node-1",
|
||||
"name_servers": "\"10.20.0.2\"",
|
||||
"name_servers_search": "\"domain.tld\"",
|
||||
"netboot_enabled": "1",
|
||||
"power_address": "10.20.0.253",
|
||||
"power_pass": "/root/.ssh/bootstrap.rsa",
|
||||
"power_type": "ssh",
|
||||
"power_user": "root",
|
||||
"profile": "pro_fi-le",
|
||||
"slave_name": "node-1",
|
||||
"uid": "1",
|
||||
"partitioning": {
|
||||
"fss": [
|
||||
{
|
||||
"device": "/dev/sda3",
|
||||
"fs_label": "",
|
||||
"fs_options": "",
|
||||
"fs_type": "ext2",
|
||||
"mount": "/boot"
|
||||
},
|
||||
{
|
||||
"device": "/dev/sda4",
|
||||
"fs_label": "",
|
||||
"fs_options": "",
|
||||
"fs_type": "ext2",
|
||||
"mount": "/tmp"
|
||||
},
|
||||
{
|
||||
"device": "/dev/mapper/os-root",
|
||||
"fs_label": "",
|
||||
"fs_options": "",
|
||||
"fs_type": "ext4",
|
||||
"mount": "/"
|
||||
},
|
||||
{
|
||||
"device": "/dev/mapper/os-swap",
|
||||
"fs_label": "",
|
||||
"fs_options": "",
|
||||
"fs_type": "swap",
|
||||
"mount": "swap"
|
||||
},
|
||||
{
|
||||
"device": "/dev/mapper/image-glance",
|
||||
"fs_label": "",
|
||||
"fs_options": "",
|
||||
"fs_type": "xfs",
|
||||
"mount": "/var/lib/glance"
|
||||
}
|
||||
],
|
||||
"lvs": [
|
||||
{
|
||||
"name": "root",
|
||||
"size": 15360,
|
||||
"vgname": "os"
|
||||
},
|
||||
{
|
||||
"name": "swap",
|
||||
"size": 4014,
|
||||
"vgname": "os"
|
||||
},
|
||||
{
|
||||
"name": "glance",
|
||||
"size": 175347,
|
||||
"vgname": "image"
|
||||
}
|
||||
],
|
||||
"mds": [],
|
||||
"parteds": [
|
||||
{
|
||||
"label": "gpt",
|
||||
"name": "/dev/sdb",
|
||||
"partitions": [
|
||||
{
|
||||
"begin": 1,
|
||||
"configdrive": false,
|
||||
"count": 1,
|
||||
"device": "/dev/sdb",
|
||||
"end": 25,
|
||||
"flags": [
|
||||
"bios_grub"
|
||||
],
|
||||
"guid": null,
|
||||
"name": "/dev/sdb1",
|
||||
"partition_type": "primary"
|
||||
},
|
||||
{
|
||||
"begin": 25,
|
||||
"configdrive": false,
|
||||
"count": 2,
|
||||
"device": "/dev/sdb",
|
||||
"end": 225,
|
||||
"flags": [],
|
||||
"guid": null,
|
||||
"name": "/dev/sdb2",
|
||||
"partition_type": "primary"
|
||||
},
|
||||
{
|
||||
"begin": 225,
|
||||
"configdrive": false,
|
||||
"count": 3,
|
||||
"device": "/dev/sdb",
|
||||
"end": 65196,
|
||||
"flags": [],
|
||||
"guid": null,
|
||||
"name": "/dev/sdb3",
|
||||
"partition_type": "primary"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "gpt",
|
||||
"name": "/dev/sda",
|
||||
"partitions": [
|
||||
{
|
||||
"begin": 1,
|
||||
"configdrive": false,
|
||||
"count": 1,
|
||||
"device": "/dev/sda",
|
||||
"end": 25,
|
||||
"flags": [
|
||||
"bios_grub"
|
||||
],
|
||||
"guid": null,
|
||||
"name": "/dev/sda1",
|
||||
"partition_type": "primary"
|
||||
},
|
||||
{
|
||||
"begin": 25,
|
||||
"configdrive": false,
|
||||
"count": 2,
|
||||
"device": "/dev/sda",
|
||||
"end": 225,
|
||||
"flags": [],
|
||||
"guid": null,
|
||||
"name": "/dev/sda2",
|
||||
"partition_type": "primary"
|
||||
},
|
||||
{
|
||||
"begin": 225,
|
||||
"configdrive": false,
|
||||
"count": 3,
|
||||
"device": "/dev/sda",
|
||||
"end": 425,
|
||||
"flags": [],
|
||||
"guid": null,
|
||||
"name": "/dev/sda3",
|
||||
"partition_type": "primary"
|
||||
},
|
||||
{
|
||||
"begin": 425,
|
||||
"configdrive": false,
|
||||
"count": 4,
|
||||
"device": "/dev/sda",
|
||||
"end": 625,
|
||||
"flags": [],
|
||||
"guid": "fake_guid",
|
||||
"name": "/dev/sda4",
|
||||
"partition_type": "primary"
|
||||
},
|
||||
{
|
||||
"begin": 625,
|
||||
"configdrive": false,
|
||||
"count": 5,
|
||||
"device": "/dev/sda",
|
||||
"end": 20063,
|
||||
"flags": [],
|
||||
"guid": null,
|
||||
"name": "/dev/sda5",
|
||||
"partition_type": "primary"
|
||||
},
|
||||
{
|
||||
"begin": 20063,
|
||||
"configdrive": false,
|
||||
"count": 6,
|
||||
"device": "/dev/sda",
|
||||
"end": 65660,
|
||||
"flags": [],
|
||||
"guid": null,
|
||||
"name": "/dev/sda6",
|
||||
"partition_type": "primary"
|
||||
},
|
||||
{
|
||||
"begin": 65660,
|
||||
"configdrive": true,
|
||||
"count": 7,
|
||||
"device": "/dev/sda",
|
||||
"end": 65680,
|
||||
"flags": [],
|
||||
"guid": null,
|
||||
"name": "/dev/sda7",
|
||||
"partition_type": "primary"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"pvs": [
|
||||
{
|
||||
"metadatacopies": 2,
|
||||
"metadatasize": 28,
|
||||
"name": "/dev/sda5"
|
||||
},
|
||||
{
|
||||
"metadatacopies": 2,
|
||||
"metadatasize": 28,
|
||||
"name": "/dev/sda6"
|
||||
},
|
||||
{
|
||||
"metadatacopies": 2,
|
||||
"metadatasize": 28,
|
||||
"name": "/dev/sdb3"
|
||||
},
|
||||
{
|
||||
"metadatacopies": 2,
|
||||
"metadatasize": 28,
|
||||
"name": "/dev/sdc3"
|
||||
}
|
||||
],
|
||||
"vgs": [
|
||||
{
|
||||
"name": "image",
|
||||
"pvnames": [
|
||||
"/dev/sda6",
|
||||
"/dev/sdb3",
|
||||
"/dev/sdc3"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "os",
|
||||
"pvnames": [
|
||||
"/dev/sda5"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
@ -19,6 +19,7 @@ import signal
|
||||
from oslo.config import cfg
|
||||
from oslotest import base as test_base
|
||||
|
||||
from fuel_agent.drivers import nailgun
|
||||
from fuel_agent import errors
|
||||
from fuel_agent import manager
|
||||
from fuel_agent import objects
|
||||
@ -438,7 +439,7 @@ class TestManager(test_base.BaseTestCase):
|
||||
mock_fu):
|
||||
mock_os.path.islink.return_value = True
|
||||
mock_utils.execute.return_value = (None, None)
|
||||
self.mgr.driver.partition_scheme = objects.PartitionScheme()
|
||||
self.mgr.driver._partition_scheme = objects.PartitionScheme()
|
||||
self.mgr.mount_target('fake_chroot')
|
||||
mock_open.assert_called_once_with('fake_chroot/etc/mtab', 'wb')
|
||||
mock_os.path.islink.assert_called_once_with('fake_chroot/etc/mtab')
|
||||
@ -451,7 +452,7 @@ class TestManager(test_base.BaseTestCase):
|
||||
@mock.patch('fuel_agent.manager.os', create=True)
|
||||
def test_mount_target(self, mock_os, mock_open, mock_utils, mock_fu):
|
||||
mock_os.path.islink.return_value = False
|
||||
self.mgr.driver.partition_scheme = objects.PartitionScheme()
|
||||
self.mgr.driver._partition_scheme = objects.PartitionScheme()
|
||||
self.mgr.driver.partition_scheme.add_fs(
|
||||
device='fake', mount='/var/lib', fs_type='xfs')
|
||||
self.mgr.driver.partition_scheme.add_fs(
|
||||
@ -500,7 +501,7 @@ none /run/shm tmpfs rw,nosuid,nodev 0 0"""
|
||||
|
||||
@mock.patch('fuel_agent.manager.fu', create=True)
|
||||
def test_umount_target(self, mock_fu):
|
||||
self.mgr.driver.partition_scheme = objects.PartitionScheme()
|
||||
self.mgr.driver._partition_scheme = objects.PartitionScheme()
|
||||
self.mgr.driver.partition_scheme.add_fs(
|
||||
device='fake', mount='/var/lib', fs_type='xfs')
|
||||
self.mgr.driver.partition_scheme.add_fs(
|
||||
@ -522,6 +523,38 @@ none /run/shm tmpfs rw,nosuid,nodev 0 0"""
|
||||
mock.call('fake_chroot/', try_lazy_umount=True)],
|
||||
mock_fu.umount_fs.call_args_list)
|
||||
|
||||
|
||||
class TestImageBuild(test_base.BaseTestCase):
|
||||
|
||||
@mock.patch('yaml.load')
|
||||
@mock.patch.object(utils, 'init_http_request')
|
||||
@mock.patch.object(utils, 'get_driver')
|
||||
def setUp(self, mock_driver, mock_http, mock_yaml):
|
||||
super(self.__class__, self).setUp()
|
||||
mock_driver.return_value = nailgun.NailgunBuildImage
|
||||
image_conf = {
|
||||
"image_data": {
|
||||
"/": {
|
||||
"container": "gzip",
|
||||
"format": "ext4",
|
||||
"uri": "http:///centos_65_x86_64.img.gz",
|
||||
},
|
||||
},
|
||||
"output": "/var/www/nailgun/targetimages",
|
||||
"repos": [
|
||||
{
|
||||
"name": "repo",
|
||||
"uri": "http://some",
|
||||
'type': 'deb',
|
||||
'suite': '/',
|
||||
'section': '',
|
||||
'priority': 1001
|
||||
}
|
||||
],
|
||||
"codename": "trusty"
|
||||
}
|
||||
self.mgr = manager.Manager(image_conf)
|
||||
|
||||
@mock.patch('fuel_agent.manager.bu', create=True)
|
||||
@mock.patch('fuel_agent.manager.fu', create=True)
|
||||
@mock.patch('fuel_agent.manager.utils', create=True)
|
||||
@ -540,17 +573,17 @@ none /run/shm tmpfs rw,nosuid,nodev 0 0"""
|
||||
|
||||
loops = [objects.Loop(), objects.Loop()]
|
||||
|
||||
self.mgr.driver.image_scheme = objects.ImageScheme([
|
||||
self.mgr.driver._image_scheme = objects.ImageScheme([
|
||||
objects.Image('file:///fake/img.img.gz', loops[0], 'ext4', 'gzip'),
|
||||
objects.Image('file:///fake/img-boot.img.gz',
|
||||
loops[1], 'ext2', 'gzip')])
|
||||
self.mgr.driver.partition_scheme = objects.PartitionScheme()
|
||||
self.mgr.driver._partition_scheme = objects.PartitionScheme()
|
||||
self.mgr.driver.partition_scheme.add_fs(
|
||||
device=loops[0], mount='/', fs_type='ext4')
|
||||
self.mgr.driver.partition_scheme.add_fs(
|
||||
device=loops[1], mount='/boot', fs_type='ext2')
|
||||
self.mgr.driver.metadata_uri = 'file:///fake/img.yaml'
|
||||
self.mgr.driver.operating_system = objects.Ubuntu(
|
||||
self.mgr.driver._operating_system = objects.Ubuntu(
|
||||
repos=[
|
||||
objects.DEBRepo('ubuntu', 'http://fakeubuntu',
|
||||
'trusty', 'fakesection', priority=900),
|
||||
|
@ -12,12 +12,12 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import mock
|
||||
import os
|
||||
|
||||
import mock
|
||||
import six
|
||||
from six.moves.urllib.parse import urlsplit
|
||||
|
||||
from oslotest import base as test_base
|
||||
import unittest2
|
||||
|
||||
from fuel_agent.drivers.nailgun import NailgunBuildImage
|
||||
from fuel_agent import errors
|
||||
@ -100,51 +100,45 @@ IMAGE_DATA_SAMPLE = {
|
||||
}
|
||||
|
||||
|
||||
class TestNailgunBuildImage(test_base.BaseTestCase):
|
||||
class TestNailgunBuildImage(unittest2.TestCase):
|
||||
|
||||
def test_default_trusty_packages(self):
|
||||
self.assertEqual(NailgunBuildImage.DEFAULT_TRUSTY_PACKAGES,
|
||||
DEFAULT_TRUSTY_PACKAGES)
|
||||
|
||||
@mock.patch.object(NailgunBuildImage, '__init__')
|
||||
def test_parse_operating_system_error_bad_codename(self, mock_init):
|
||||
mock_init.return_value = None
|
||||
driver = NailgunBuildImage()
|
||||
driver.data = {'codename': 'not-trusty'}
|
||||
self.assertRaises(errors.WrongInputDataError,
|
||||
driver.parse_operating_system)
|
||||
@mock.patch.object(NailgunBuildImage, 'parse_schemes')
|
||||
def test_parse_operating_system_error_bad_codename(self,
|
||||
mock_parse_schemes):
|
||||
with self.assertRaises(errors.WrongInputDataError):
|
||||
data = {'codename': 'not-trusty'}
|
||||
NailgunBuildImage(data)
|
||||
|
||||
@mock.patch('fuel_agent.objects.Ubuntu')
|
||||
@mock.patch.object(NailgunBuildImage, '__init__')
|
||||
def test_parse_operating_system_packages_given(self, mock_init, mock_ub):
|
||||
mock_init.return_value = None
|
||||
@mock.patch.object(NailgunBuildImage, 'parse_schemes')
|
||||
def test_parse_operating_system_packages_given(self, mock_parse_schemes,
|
||||
mock_ub):
|
||||
data = {
|
||||
'repos': [],
|
||||
'codename': 'trusty',
|
||||
'packages': ['pack']
|
||||
}
|
||||
driver = NailgunBuildImage()
|
||||
driver.data = data
|
||||
mock_ub_instance = mock_ub.return_value
|
||||
mock_ub_instance.packages = data['packages']
|
||||
driver.parse_operating_system()
|
||||
driver = NailgunBuildImage(data)
|
||||
mock_ub.assert_called_once_with(repos=[], packages=data['packages'])
|
||||
self.assertEqual(driver.operating_system.packages, data['packages'])
|
||||
|
||||
@mock.patch('fuel_agent.objects.Ubuntu')
|
||||
@mock.patch.object(NailgunBuildImage, '__init__')
|
||||
@mock.patch.object(NailgunBuildImage, 'parse_schemes')
|
||||
def test_parse_operating_system_packages_not_given(
|
||||
self, mock_init, mock_ub):
|
||||
mock_init.return_value = None
|
||||
self, mock_parse_schemes, mock_ub):
|
||||
data = {
|
||||
'repos': [],
|
||||
'codename': 'trusty'
|
||||
}
|
||||
driver = NailgunBuildImage()
|
||||
driver.data = data
|
||||
mock_ub_instance = mock_ub.return_value
|
||||
mock_ub_instance.packages = NailgunBuildImage.DEFAULT_TRUSTY_PACKAGES
|
||||
driver.parse_operating_system()
|
||||
driver = NailgunBuildImage(data)
|
||||
mock_ub.assert_called_once_with(
|
||||
repos=[], packages=NailgunBuildImage.DEFAULT_TRUSTY_PACKAGES)
|
||||
self.assertEqual(driver.operating_system.packages,
|
||||
@ -152,15 +146,13 @@ class TestNailgunBuildImage(test_base.BaseTestCase):
|
||||
|
||||
@mock.patch('fuel_agent.objects.DEBRepo')
|
||||
@mock.patch('fuel_agent.objects.Ubuntu')
|
||||
@mock.patch.object(NailgunBuildImage, '__init__')
|
||||
def test_parse_operating_system_repos(self, mock_init, mock_ub, mock_deb):
|
||||
mock_init.return_value = None
|
||||
@mock.patch.object(NailgunBuildImage, 'parse_schemes')
|
||||
def test_parse_operating_system_repos(self, mock_parse_schemes, mock_ub,
|
||||
mock_deb):
|
||||
data = {
|
||||
'repos': REPOS_SAMPLE,
|
||||
'codename': 'trusty'
|
||||
}
|
||||
driver = NailgunBuildImage()
|
||||
driver.data = data
|
||||
|
||||
mock_deb_expected_calls = []
|
||||
repos = []
|
||||
@ -174,7 +166,7 @@ class TestNailgunBuildImage(test_base.BaseTestCase):
|
||||
}
|
||||
mock_deb_expected_calls.append(mock.call(**kwargs))
|
||||
repos.append(objects.DEBRepo(**kwargs))
|
||||
driver.parse_operating_system()
|
||||
driver = NailgunBuildImage(data)
|
||||
mock_ub_instance = mock_ub.return_value
|
||||
mock_ub_instance.repos = repos
|
||||
mock_ub.assert_called_once_with(
|
||||
@ -185,21 +177,18 @@ class TestNailgunBuildImage(test_base.BaseTestCase):
|
||||
|
||||
@mock.patch('fuel_agent.drivers.nailgun.objects.Loop')
|
||||
@mock.patch('fuel_agent.objects.Image')
|
||||
@mock.patch('fuel_agent.objects.Fs')
|
||||
@mock.patch('fuel_agent.objects.FS')
|
||||
@mock.patch('fuel_agent.objects.PartitionScheme')
|
||||
@mock.patch('fuel_agent.objects.ImageScheme')
|
||||
@mock.patch.object(NailgunBuildImage, '__init__')
|
||||
@mock.patch.object(NailgunBuildImage, 'parse_operating_system')
|
||||
def test_parse_schemes(
|
||||
self, mock_init, mock_imgsch, mock_partsch,
|
||||
self, mock_parse_os, mock_imgsch, mock_partsch,
|
||||
mock_fs, mock_img, mock_loop):
|
||||
mock_init.return_value = None
|
||||
data = {
|
||||
'image_data': IMAGE_DATA_SAMPLE,
|
||||
'output': '/some/local/path',
|
||||
}
|
||||
driver = NailgunBuildImage()
|
||||
driver.data = data
|
||||
driver.parse_schemes()
|
||||
driver = NailgunBuildImage(data)
|
||||
|
||||
mock_fs_expected_calls = []
|
||||
mock_img_expected_calls = []
|
||||
@ -223,7 +212,7 @@ class TestNailgunBuildImage(test_base.BaseTestCase):
|
||||
'fs_type': image['format']
|
||||
}
|
||||
mock_fs_expected_calls.append(mock.call(**fs_kwargs))
|
||||
fss.append(objects.Fs(**fs_kwargs))
|
||||
fss.append(objects.FS(**fs_kwargs))
|
||||
|
||||
if mount == '/':
|
||||
metadata_filename = filename.split('.', 1)[0] + '.yaml'
|
||||
|
@ -14,16 +14,17 @@
|
||||
|
||||
import mock
|
||||
|
||||
from oslotest import base as test_base
|
||||
import unittest2
|
||||
|
||||
from fuel_agent import errors
|
||||
from fuel_agent.objects import partition
|
||||
|
||||
|
||||
class TestMD(test_base.BaseTestCase):
|
||||
class TestMultipleDevice(unittest2.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestMD, self).setUp()
|
||||
self.md = partition.Md('name', 'level')
|
||||
super(self.__class__, self).setUp()
|
||||
self.md = partition.MD(name='name', level='level')
|
||||
|
||||
def test_add_device_ok(self):
|
||||
self.assertEqual(0, len(self.md.devices))
|
||||
@ -59,8 +60,22 @@ class TestMD(test_base.BaseTestCase):
|
||||
self.assertRaises(errors.MDDeviceDuplicationError, self.md.add_spare,
|
||||
'device')
|
||||
|
||||
def test_conversion(self):
|
||||
self.md.add_device('device_a')
|
||||
self.md.add_spare('device_b')
|
||||
serialized = self.md.to_dict()
|
||||
assert serialized == {
|
||||
'name': 'name',
|
||||
'level': 'level',
|
||||
'devices': ['device_a', ],
|
||||
'spares': ['device_b', ],
|
||||
}
|
||||
new_md = partition.MD.from_dict(serialized)
|
||||
assert serialized == new_md.to_dict()
|
||||
|
||||
|
||||
class TestPartition(unittest2.TestCase):
|
||||
|
||||
class TestPartition(test_base.BaseTestCase):
|
||||
def setUp(self):
|
||||
super(TestPartition, self).setUp()
|
||||
self.pt = partition.Partition('name', 'count', 'device', 'begin',
|
||||
@ -72,8 +87,27 @@ class TestPartition(test_base.BaseTestCase):
|
||||
self.assertEqual(1, len(self.pt.flags))
|
||||
self.assertIn('fake_flag', self.pt.flags)
|
||||
|
||||
def test_conversion(self):
|
||||
self.pt.flags.append('some_flag')
|
||||
self.pt.guid = 'some_guid'
|
||||
serialized = self.pt.to_dict()
|
||||
assert serialized == {
|
||||
'begin': 'begin',
|
||||
'configdrive': False,
|
||||
'count': 'count',
|
||||
'device': 'device',
|
||||
'end': 'end',
|
||||
'flags': ['some_flag', ],
|
||||
'guid': 'some_guid',
|
||||
'name': 'name',
|
||||
'partition_type': 'partition_type',
|
||||
}
|
||||
new_pt = partition.Partition.from_dict(serialized)
|
||||
assert serialized == new_pt.to_dict()
|
||||
|
||||
|
||||
class TestPartitionScheme(unittest2.TestCase):
|
||||
|
||||
class TestPartitionScheme(test_base.BaseTestCase):
|
||||
def setUp(self):
|
||||
super(TestPartitionScheme, self).setUp()
|
||||
self.p_scheme = partition.PartitionScheme()
|
||||
@ -83,30 +117,30 @@ class TestPartitionScheme(test_base.BaseTestCase):
|
||||
self.p_scheme.root_device)
|
||||
|
||||
def test_fs_by_device(self):
|
||||
expected_fs = partition.Fs('device')
|
||||
expected_fs = partition.FS('device')
|
||||
self.p_scheme.fss.append(expected_fs)
|
||||
self.p_scheme.fss.append(partition.Fs('wrong_device'))
|
||||
self.p_scheme.fss.append(partition.FS('wrong_device'))
|
||||
actual_fs = self.p_scheme.fs_by_device('device')
|
||||
self.assertEqual(expected_fs, actual_fs)
|
||||
|
||||
def test_fs_by_mount(self):
|
||||
expected_fs = partition.Fs('d', mount='mount')
|
||||
expected_fs = partition.FS('d', mount='mount')
|
||||
self.p_scheme.fss.append(expected_fs)
|
||||
self.p_scheme.fss.append(partition.Fs('w_d', mount='wrong_mount'))
|
||||
self.p_scheme.fss.append(partition.FS('w_d', mount='wrong_mount'))
|
||||
actual_fs = self.p_scheme.fs_by_mount('mount')
|
||||
self.assertEqual(expected_fs, actual_fs)
|
||||
|
||||
def test_pv_by_name(self):
|
||||
expected_pv = partition.Pv('pv')
|
||||
expected_pv = partition.PV('pv')
|
||||
self.p_scheme.pvs.append(expected_pv)
|
||||
self.p_scheme.pvs.append(partition.Pv('wrong_pv'))
|
||||
self.p_scheme.pvs.append(partition.PV('wrong_pv'))
|
||||
actual_pv = self.p_scheme.pv_by_name('pv')
|
||||
self.assertEqual(expected_pv, actual_pv)
|
||||
|
||||
def test_vg_by_name(self):
|
||||
expected_vg = partition.Vg('vg')
|
||||
expected_vg = partition.VG('vg')
|
||||
self.p_scheme.vgs.append(expected_vg)
|
||||
self.p_scheme.vgs.append(partition.Vg('wrong_vg'))
|
||||
self.p_scheme.vgs.append(partition.VG('wrong_vg'))
|
||||
actual_vg = self.p_scheme.vg_by_name('vg')
|
||||
self.assertEqual(expected_vg, actual_vg)
|
||||
|
||||
@ -123,33 +157,33 @@ class TestPartitionScheme(test_base.BaseTestCase):
|
||||
|
||||
def test_md_next_name_fail(self):
|
||||
self.p_scheme.mds = [
|
||||
partition.Md('/dev/md%s' % x, 'level') for x in range(0, 128)]
|
||||
partition.MD('/dev/md%s' % x, 'level') for x in range(0, 128)]
|
||||
self.assertRaises(errors.MDAlreadyExistsError,
|
||||
self.p_scheme.md_next_name)
|
||||
|
||||
def test_md_by_name(self):
|
||||
self.assertEqual(0, len(self.p_scheme.mds))
|
||||
expected_md = partition.Md('name', 'level')
|
||||
expected_md = partition.MD('name', 'level')
|
||||
self.p_scheme.mds.append(expected_md)
|
||||
self.p_scheme.mds.append(partition.Md('wrong_name', 'level'))
|
||||
self.p_scheme.mds.append(partition.MD('wrong_name', 'level'))
|
||||
self.assertEqual(expected_md, self.p_scheme.md_by_name('name'))
|
||||
|
||||
def test_md_by_mount(self):
|
||||
self.assertEqual(0, len(self.p_scheme.mds))
|
||||
self.assertEqual(0, len(self.p_scheme.fss))
|
||||
expected_md = partition.Md('name', 'level')
|
||||
expected_fs = partition.Fs('name', mount='mount')
|
||||
expected_md = partition.MD('name', 'level')
|
||||
expected_fs = partition.FS('name', mount='mount')
|
||||
self.p_scheme.mds.append(expected_md)
|
||||
self.p_scheme.fss.append(expected_fs)
|
||||
self.p_scheme.fss.append(partition.Fs('wrong_name',
|
||||
self.p_scheme.fss.append(partition.FS('wrong_name',
|
||||
mount='wrong_mount'))
|
||||
self.assertEqual(expected_md, self.p_scheme.md_by_mount('mount'))
|
||||
|
||||
def test_md_attach_by_mount_md_exists(self):
|
||||
self.assertEqual(0, len(self.p_scheme.mds))
|
||||
self.assertEqual(0, len(self.p_scheme.fss))
|
||||
expected_md = partition.Md('name', 'level')
|
||||
expected_fs = partition.Fs('name', mount='mount')
|
||||
expected_md = partition.MD('name', 'level')
|
||||
expected_fs = partition.FS('name', mount='mount')
|
||||
self.p_scheme.mds.append(expected_md)
|
||||
self.p_scheme.fss.append(expected_fs)
|
||||
actual_md = self.p_scheme.md_attach_by_mount('device', 'mount')
|
||||
@ -171,7 +205,7 @@ class TestPartitionScheme(test_base.BaseTestCase):
|
||||
self.assertEqual('-F', self.p_scheme.fss[0].options)
|
||||
|
||||
|
||||
class TestParted(test_base.BaseTestCase):
|
||||
class TestParted(unittest2.TestCase):
|
||||
def setUp(self):
|
||||
super(TestParted, self).setUp()
|
||||
self.prtd = partition.Parted('name', 'label')
|
||||
@ -254,3 +288,99 @@ class TestParted(test_base.BaseTestCase):
|
||||
'begin', 'end', 'primary')]
|
||||
self.prtd.partitions.extend(expected_partitions)
|
||||
self.assertEqual(expected_partitions, self.prtd.primary)
|
||||
|
||||
def test_conversion(self):
|
||||
prt = partition.Partition(
|
||||
name='name',
|
||||
count='count',
|
||||
device='device',
|
||||
begin='begin',
|
||||
end='end',
|
||||
partition_type='primary'
|
||||
)
|
||||
self.prtd.partitions.append(prt)
|
||||
serialized = self.prtd.to_dict()
|
||||
assert serialized == {
|
||||
'label': 'label',
|
||||
'name': 'name',
|
||||
'partitions': [
|
||||
prt.to_dict(),
|
||||
],
|
||||
'install_bootloader': False,
|
||||
}
|
||||
new_prtd = partition.Parted.from_dict(serialized)
|
||||
assert serialized == new_prtd.to_dict()
|
||||
|
||||
|
||||
class TestLogicalVolume(unittest2.TestCase):
|
||||
|
||||
def test_conversion(self):
|
||||
lv = partition.LV(
|
||||
name='lv-name',
|
||||
vgname='vg-name',
|
||||
size=1234
|
||||
)
|
||||
serialized = lv.to_dict()
|
||||
assert serialized == {
|
||||
'name': 'lv-name',
|
||||
'vgname': 'vg-name',
|
||||
'size': 1234,
|
||||
}
|
||||
new_lv = partition.LV.from_dict(serialized)
|
||||
assert serialized == new_lv.to_dict()
|
||||
|
||||
|
||||
class TestPhisicalVolume(unittest2.TestCase):
|
||||
|
||||
def test_conversion(self):
|
||||
pv = partition.PV(
|
||||
name='pv-name',
|
||||
metadatasize=987,
|
||||
metadatacopies=112,
|
||||
)
|
||||
serialized = pv.to_dict()
|
||||
assert serialized == {
|
||||
'name': 'pv-name',
|
||||
'metadatasize': 987,
|
||||
'metadatacopies': 112,
|
||||
}
|
||||
new_pv = partition.PV.from_dict(serialized)
|
||||
assert serialized == new_pv.to_dict()
|
||||
|
||||
|
||||
class TestVolumesGroup(unittest2.TestCase):
|
||||
|
||||
def test_conversion(self):
|
||||
vg = partition.VG(
|
||||
name='vg-name',
|
||||
pvnames=['pv-name-a', ]
|
||||
)
|
||||
serialized = vg.to_dict()
|
||||
assert serialized == {
|
||||
'name': 'vg-name',
|
||||
'pvnames': ['pv-name-a', ]
|
||||
}
|
||||
new_vg = partition.VG.from_dict(serialized)
|
||||
assert serialized == new_vg.to_dict()
|
||||
|
||||
|
||||
class TestFileSystem(unittest2.TestCase):
|
||||
|
||||
def test_conversion(self):
|
||||
fs = partition.FS(
|
||||
device='some-device',
|
||||
mount='/mount',
|
||||
fs_type='type',
|
||||
fs_options='some-option',
|
||||
fs_label='some-label',
|
||||
)
|
||||
serialized = fs.to_dict()
|
||||
assert serialized == {
|
||||
'device': 'some-device',
|
||||
'mount': '/mount',
|
||||
'fs_type': 'type',
|
||||
'fs_options': 'some-option',
|
||||
'fs_label': 'some-label',
|
||||
}
|
||||
new_fs = partition.FS.from_dict(serialized)
|
||||
assert serialized == new_fs.to_dict()
|
||||
|
224
fuel_agent/tests/test_simple_nailgun_driver.py
Normal file
224
fuel_agent/tests/test_simple_nailgun_driver.py
Normal file
@ -0,0 +1,224 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright 2015 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 mock
|
||||
import requests_mock
|
||||
import unittest2
|
||||
|
||||
from fuel_agent.drivers import simple
|
||||
from fuel_agent import objects
|
||||
from fuel_agent.tests import base
|
||||
|
||||
|
||||
@mock.patch.multiple(
|
||||
simple.NailgunSimpleDriver,
|
||||
parse_grub=lambda x: objects.Grub(),
|
||||
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({})
|
||||
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',
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
driver = simple.NailgunSimpleDriver(lv_data)
|
||||
lv = driver.partition_scheme.lvs[0]
|
||||
assert len(driver.partition_scheme.lvs) == 1
|
||||
assert isinstance(lv, objects.LV)
|
||||
assert lv.name == 'lv-name'
|
||||
assert lv.size == 12345
|
||||
assert lv.vgname == 'vg-name'
|
||||
|
||||
def test_pv_data_is_loaded(self):
|
||||
pv_data = {
|
||||
'partitioning': {
|
||||
'pvs': [
|
||||
{
|
||||
'metadatacopies': 2,
|
||||
'metadatasize': 28,
|
||||
'name': '/dev/sda5'
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
driver = simple.NailgunSimpleDriver(pv_data)
|
||||
pv = driver.partition_scheme.pvs[0]
|
||||
assert len(driver.partition_scheme.pvs) == 1
|
||||
assert isinstance(pv, objects.PV)
|
||||
assert pv.name == '/dev/sda5'
|
||||
assert pv.metadatacopies == 2
|
||||
assert pv.metadatasize == 28
|
||||
|
||||
def test_vg_data_is_loaded(self):
|
||||
vg_data = {
|
||||
'partitioning': {
|
||||
'vgs': [
|
||||
{
|
||||
'name': 'image',
|
||||
'pvnames': [
|
||||
'/dev/sda6',
|
||||
'/dev/sdb3',
|
||||
'/dev/sdc3',
|
||||
]
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
driver = simple.NailgunSimpleDriver(vg_data)
|
||||
vg = driver.partition_scheme.vgs[0]
|
||||
assert len(driver.partition_scheme.vgs) == 1
|
||||
assert isinstance(vg, objects.VG)
|
||||
assert vg.name == 'image'
|
||||
self.assertItemsEqual(
|
||||
vg.pvnames,
|
||||
(
|
||||
'/dev/sda6',
|
||||
'/dev/sdb3',
|
||||
'/dev/sdc3',
|
||||
)
|
||||
)
|
||||
|
||||
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'
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
driver = simple.NailgunSimpleDriver(fs_data)
|
||||
fs = driver.partition_scheme.fss[0]
|
||||
assert len(driver.partition_scheme.fss) == 1
|
||||
assert isinstance(fs, objects.FS)
|
||||
assert fs.device == '/dev/sda3'
|
||||
assert fs.label == 'some-label'
|
||||
assert fs.options == 'some-options'
|
||||
assert fs.type == 'ext2'
|
||||
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'
|
||||
},
|
||||
]
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
driver = simple.NailgunSimpleDriver(parted_data)
|
||||
parted = driver.partition_scheme.parteds[0]
|
||||
partition = parted.partitions[0]
|
||||
assert len(driver.partition_scheme.parteds) == 1
|
||||
assert isinstance(parted, objects.Parted)
|
||||
assert parted.label == 'gpt'
|
||||
assert parted.name == '/dev/sdb'
|
||||
assert len(parted.partitions) == 1
|
||||
assert partition.begin == 1
|
||||
assert partition.configdrive is False
|
||||
assert partition.count == 1
|
||||
assert partition.device == '/dev/sdb'
|
||||
assert partition.end == 25
|
||||
self.assertItemsEqual(partition.flags, ['bios_grub', 'xyz'])
|
||||
assert partition.guid is None
|
||||
assert partition.name == '/dev/sdb1'
|
||||
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',
|
||||
]
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
driver = simple.NailgunSimpleDriver(md_data)
|
||||
md = driver.partition_scheme.mds[0]
|
||||
assert len(driver.partition_scheme.mds) == 1
|
||||
assert isinstance(md, objects.MD)
|
||||
assert md.name == 'some-raid'
|
||||
assert md.level == 1
|
||||
self.assertItemsEqual(md.devices, ['/dev/sda', '/dev/sdc'])
|
||||
self.assertItemsEqual(md.spares, ['/dev/sdb', '/dev/sdd'])
|
||||
|
||||
|
||||
@requests_mock.mock()
|
||||
class TestFullDataRead(unittest2.TestCase):
|
||||
|
||||
PROVISION_DATA = base.load_fixture('simple_nailgun_driver.json')
|
||||
|
||||
def test_read_with_no_error(self, mock_requests):
|
||||
mock_requests.get('http://fake.host.org:123/imgs/fake_image.img.gz',
|
||||
text='{}')
|
||||
driver = simple.NailgunSimpleDriver(self.PROVISION_DATA)
|
||||
scheme = driver.partition_scheme
|
||||
assert len(scheme.fss) == 5
|
||||
assert len(scheme.lvs) == 3
|
||||
assert len(scheme.mds) == 0
|
||||
assert len(scheme.parteds) == 2
|
||||
assert len(scheme.pvs) == 4
|
||||
assert len(scheme.vgs) == 2
|
39
fuel_agent/utils/decorators.py
Normal file
39
fuel_agent/utils/decorators.py
Normal file
@ -0,0 +1,39 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright 2015 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.
|
||||
|
||||
|
||||
class abstractclassmethod(classmethod):
|
||||
"""A decorator indicating abstract classmethods.
|
||||
|
||||
Similar to abstractmethod.
|
||||
|
||||
Usage:
|
||||
|
||||
class C(object):
|
||||
__metaclass__ = abc.ABCMeta
|
||||
|
||||
@abstractclassmethod
|
||||
def my_abstract_classmethod(cls, ...):
|
||||
...
|
||||
|
||||
Copied from Python 3.2
|
||||
"""
|
||||
|
||||
__isabstractmethod__ = True
|
||||
|
||||
def __init__(self, callable):
|
||||
callable.__isabstractmethod__ = True
|
||||
super(abstractclassmethod, self).__init__(callable)
|
@ -24,6 +24,7 @@ console_scripts =
|
||||
|
||||
fuel_agent.drivers =
|
||||
nailgun = fuel_agent.drivers.nailgun:Nailgun
|
||||
nailgun_simple = fuel_agent.drivers.simple:NailgunSimpleDriver
|
||||
nailgun_build_image = fuel_agent.drivers.nailgun:NailgunBuildImage
|
||||
|
||||
[pbr]
|
||||
|
@ -1,6 +1,9 @@
|
||||
hacking>=0.8.0,<0.9
|
||||
mock==1.0.1
|
||||
# TODO(prmtl): remove oslotest and (probably) testools in favor of unittest2
|
||||
oslotest==1.0
|
||||
testtools>=0.9.34
|
||||
unittest2==1.0.1
|
||||
pytest>=2.7.2
|
||||
pytest-cov>=1.8.1
|
||||
requests-mock>=0.6
|
||||
|
Loading…
Reference in New Issue
Block a user