Split capsule code out of container driver

Introduce a capsule driver in parallel with container driver.
Capsule logic goes to capsule driver and container logic goes to
container driver.

Change-Id: I0c3efdb0068cfa89b8cfc6cad68425838cd2e50f
This commit is contained in:
Hongbin Lu 2020-01-19 22:22:29 +00:00
parent 83dbf2d5c4
commit 624eebbc8a
11 changed files with 127 additions and 44 deletions

View File

@ -72,6 +72,10 @@ zun.container.driver =
docker = zun.container.docker.driver:DockerDriver docker = zun.container.docker.driver:DockerDriver
fake = zun.tests.unit.container.fake_driver:FakeDriver fake = zun.tests.unit.container.fake_driver:FakeDriver
zun.capsule.driver =
docker = zun.container.docker.driver:DockerDriver
fake = zun.tests.unit.container.fake_driver:FakeDriver
zun.image.driver = zun.image.driver =
glance = zun.image.glance.driver:GlanceDriver glance = zun.image.glance.driver:GlanceDriver
docker = zun.image.docker.driver:DockerDriver docker = zun.image.docker.driver:DockerDriver

View File

@ -350,7 +350,7 @@ class CapsuleController(base.Controller):
kwargs['extra_spec'] = extra_spec kwargs['extra_spec'] = extra_spec
kwargs['requested_networks'] = requested_networks kwargs['requested_networks'] = requested_networks
kwargs['requested_volumes'] = requested_volumes kwargs['requested_volumes'] = requested_volumes
kwargs['run'] = True kwargs['run'] = False
compute_api.container_create(context, new_capsule, **kwargs) compute_api.container_create(context, new_capsule, **kwargs)
# Set the HTTP Location Header # Set the HTTP Location Header
pecan.response.location = link.build_url('capsules', pecan.response.location = link.build_url('capsules',
@ -406,7 +406,6 @@ class CapsuleController(base.Controller):
capsule.task_state = consts.CONTAINER_DELETING capsule.task_state = consts.CONTAINER_DELETING
capsule.save(context) capsule.save(context)
if capsule.host: if capsule.host:
compute_api.container_stop(context, capsule, 10)
compute_api.container_delete(context, capsule) compute_api.container_delete(context, capsule)
else: else:
merged_containers = capsule.containers + capsule.init_containers merged_containers = capsule.containers + capsule.init_containers

View File

@ -36,9 +36,10 @@ COMPUTE_RESOURCE_SEMAPHORE = "compute_resources"
class ComputeNodeTracker(object): class ComputeNodeTracker(object):
def __init__(self, host, container_driver, reportclient): def __init__(self, host, container_driver, capsule_driver, reportclient):
self.host = host self.host = host
self.container_driver = container_driver self.container_driver = container_driver
self.capsule_driver = capsule_driver
self.compute_node = None self.compute_node = None
self.tracked_containers = {} self.tracked_containers = {}
self.old_resources = collections.defaultdict(objects.ComputeNode) self.old_resources = collections.defaultdict(objects.ComputeNode)
@ -58,6 +59,8 @@ class ComputeNodeTracker(object):
compute_node.pci_device_pools = dev_pools_obj compute_node.pci_device_pools = dev_pools_obj
def update_available_resources(self, context): def update_available_resources(self, context):
# TODO(hongbin): get available resources from capsule_driver
# and aggregates resources
resources = self.container_driver.get_available_resources() resources = self.container_driver.get_available_resources()
# We allow 'cpu_used' to be missing from the container driver, # We allow 'cpu_used' to be missing from the container driver,
# but the DB requires it to be non-null so just initialize it to 0. # but the DB requires it to be non-null so just initialize it to 0.
@ -391,6 +394,7 @@ class ComputeNodeTracker(object):
# Now get the driver's capabilities and add any supported # Now get the driver's capabilities and add any supported
# traits that are missing, and remove any existing set traits # traits that are missing, and remove any existing set traits
# that are not currently supported. # that are not currently supported.
# TODO(hongbin): get traits from capsule_driver as well
capabilities_traits = self.container_driver.capabilities_as_traits() capabilities_traits = self.container_driver.capabilities_as_traits()
for trait, supported in capabilities_traits.items(): for trait, supported in capabilities_traits.items():
if supported: if supported:

View File

@ -35,7 +35,7 @@ from zun.common.utils import wrap_exception
from zun.compute import compute_node_tracker from zun.compute import compute_node_tracker
from zun.compute import container_actions from zun.compute import container_actions
import zun.conf import zun.conf
from zun.container import driver from zun.container import driver as driver_module
from zun.image.glance import driver as glance from zun.image.glance import driver as glance
from zun.network import neutron from zun.network import neutron
from zun import objects from zun import objects
@ -50,11 +50,23 @@ class Manager(periodic_task.PeriodicTasks):
def __init__(self, container_driver=None): def __init__(self, container_driver=None):
super(Manager, self).__init__(CONF) super(Manager, self).__init__(CONF)
self.driver = driver.load_container_driver(container_driver) self.driver = driver_module.load_container_driver(container_driver)
self.capsule_driver = driver_module.load_capsule_driver()
self.host = CONF.host self.host = CONF.host
self._resource_tracker = None self._resource_tracker = None
self.reportclient = report.SchedulerReportClient() self.reportclient = report.SchedulerReportClient()
def _get_driver(self, container):
if (isinstance(container, objects.Capsule) or
isinstance(container, objects.CapsuleContainer) or
isinstance(container, objects.CapsuleInitContainer)):
return self.capsule_driver
elif isinstance(container, objects.Container):
return self.driver
else:
raise exception.ZunException('Unexpected container type: %(type)s.'
% {'type': type(container)})
def restore_running_container(self, context, container, current_status): def restore_running_container(self, context, container, current_status):
if (container.status == consts.RUNNING and if (container.status == consts.RUNNING and
current_status == consts.STOPPED): current_status == consts.STOPPED):
@ -68,6 +80,7 @@ class Manager(periodic_task.PeriodicTasks):
def init_containers(self, context): def init_containers(self, context):
containers = objects.Container.list_by_host(context, self.host) containers = objects.Container.list_by_host(context, self.host)
# TODO(hongbin): init capsules as well
local_containers, _ = self.driver.list(context) local_containers, _ = self.driver.list(context)
uuid_to_status_map = {container.uuid: container.status uuid_to_status_map = {container.uuid: container.status
for container in local_containers} for container in local_containers}
@ -162,12 +175,13 @@ class Manager(periodic_task.PeriodicTasks):
def _wait_for_volumes_available(self, context, requested_volumes, def _wait_for_volumes_available(self, context, requested_volumes,
container, timeout=60, poll_interval=1): container, timeout=60, poll_interval=1):
driver = self._get_driver(container)
start_time = time.time() start_time = time.time()
try: try:
volmaps = itertools.chain.from_iterable(requested_volumes.values()) volmaps = itertools.chain.from_iterable(requested_volumes.values())
volmap = next(volmaps) volmap = next(volmaps)
while time.time() - start_time < timeout: while time.time() - start_time < timeout:
is_available, is_error = self.driver.is_volume_available( is_available, is_error = driver.is_volume_available(
context, volmap) context, volmap)
if is_available: if is_available:
volmap = next(volmaps) volmap = next(volmaps)
@ -180,7 +194,7 @@ class Manager(periodic_task.PeriodicTasks):
for volmap in volmaps: for volmap in volmaps:
if volmap.auto_remove: if volmap.auto_remove:
try: try:
self.driver.delete_volume(context, volmap) driver.delete_volume(context, volmap)
except Exception: except Exception:
LOG.exception("Failed to delete volume") LOG.exception("Failed to delete volume")
msg = _("Volumes did not reach available status after " msg = _("Volumes did not reach available status after "
@ -197,7 +211,8 @@ class Manager(periodic_task.PeriodicTasks):
while time.time() - start_time < timeout: while time.time() - start_time < timeout:
if not volmap.auto_remove: if not volmap.auto_remove:
volmap = next(volmaps) volmap = next(volmaps)
is_deleted, is_error = self.driver.is_volume_deleted( driver = self._get_driver(container)
is_deleted, is_error = driver.is_volume_deleted(
context, volmap) context, volmap)
if is_deleted: if is_deleted:
volmap = next(volmaps) volmap = next(volmaps)
@ -212,7 +227,8 @@ class Manager(periodic_task.PeriodicTasks):
raise exception.Conflict(msg) raise exception.Conflict(msg)
def _check_support_disk_quota(self, context, container): def _check_support_disk_quota(self, context, container):
base_device_size = self.driver.get_host_default_base_size() driver = self._get_driver(container)
base_device_size = driver.get_host_default_base_size()
if base_device_size: if base_device_size:
# NOTE(kiennt): If default_base_size is not None, it means # NOTE(kiennt): If default_base_size is not None, it means
# host storage_driver is in list ['devicemapper', # host storage_driver is in list ['devicemapper',
@ -237,14 +253,14 @@ class Manager(periodic_task.PeriodicTasks):
raise exception.Invalid(msg) raise exception.Invalid(msg)
# NOTE(kiennt): Only raise Exception when user passes disk size and # NOTE(kiennt): Only raise Exception when user passes disk size and
# the disk quota feature isn't supported in host. # the disk quota feature isn't supported in host.
if not self.driver.node_support_disk_quota(): if not driver.node_support_disk_quota():
if container.disk: if container.disk:
msg = _('Your host does not support disk quota feature.') msg = _('Your host does not support disk quota feature.')
self._fail_container(context, container, msg, unset_host=True) self._fail_container(context, container, msg, unset_host=True)
raise exception.Invalid(msg) raise exception.Invalid(msg)
LOG.warning("Ignore the configured default disk size because " LOG.warning("Ignore the configured default disk size because "
"the driver does not support disk quota.") "the driver does not support disk quota.")
if self.driver.node_support_disk_quota() and not container.disk: if driver.node_support_disk_quota() and not container.disk:
container.disk = CONF.default_disk container.disk = CONF.default_disk
return return
@ -297,6 +313,7 @@ class Manager(periodic_task.PeriodicTasks):
image_pull_policy = utils.get_image_pull_policy( image_pull_policy = utils.get_image_pull_policy(
container.image_pull_policy, tag) container.image_pull_policy, tag)
try: try:
# TODO(hongbin): move image pulling logic to docker driver
image, image_loaded = self.driver.pull_image( image, image_loaded = self.driver.pull_image(
context, repo, tag, image_pull_policy, image_driver_name, context, repo, tag, image_pull_policy, image_driver_name,
registry=container.registry) registry=container.registry)
@ -327,10 +344,9 @@ class Manager(periodic_task.PeriodicTasks):
LOG.warning("The input tag is different from the tag in " LOG.warning("The input tag is different from the tag in "
"tar") "tar")
if isinstance(container, objects.Capsule): if isinstance(container, objects.Capsule):
container = self.driver.create_capsule(context, container, container = self.capsule_driver.create_capsule(
image, context, container, image, requested_networks,
requested_networks, requested_volumes)
requested_volumes)
elif isinstance(container, objects.Container): elif isinstance(container, objects.Container):
container = self.driver.create(context, container, image, container = self.driver.create(context, container, image,
requested_networks, requested_networks,
@ -397,20 +413,21 @@ class Manager(periodic_task.PeriodicTasks):
# This will happen only if there are multiple containers # This will happen only if there are multiple containers
# inside a capsule sharing the same volume. # inside a capsule sharing the same volume.
continue continue
self._attach_volume(context, volmap) self._attach_volume(context, container, volmap)
self._refresh_attached_volumes(requested_volumes, volmap) self._refresh_attached_volumes(requested_volumes, volmap)
except Exception as e: except Exception as e:
with excutils.save_and_reraise_exception(): with excutils.save_and_reraise_exception():
self._fail_container(context, container, six.text_type(e), self._fail_container(context, container, six.text_type(e),
unset_host=True) unset_host=True)
def _attach_volume(self, context, volmap): def _attach_volume(self, context, container, volmap):
driver = self._get_driver(container)
context = context.elevated() context = context.elevated()
LOG.info('Attaching volume %(volume_id)s to %(host)s', LOG.info('Attaching volume %(volume_id)s to %(host)s',
{'volume_id': volmap.cinder_volume_id, {'volume_id': volmap.cinder_volume_id,
'host': CONF.host}) 'host': CONF.host})
try: try:
self.driver.attach_volume(context, volmap) driver.attach_volume(context, volmap)
except Exception: except Exception:
with excutils.save_and_reraise_exception(): with excutils.save_and_reraise_exception():
LOG.error("Failed to attach volume %(volume_id)s to " LOG.error("Failed to attach volume %(volume_id)s to "
@ -419,7 +436,7 @@ class Manager(periodic_task.PeriodicTasks):
'container_id': volmap.container_uuid}) 'container_id': volmap.container_uuid})
if volmap.auto_remove: if volmap.auto_remove:
try: try:
self.driver.delete_volume(context, volmap) driver.delete_volume(context, volmap)
except Exception: except Exception:
LOG.exception("Failed to delete volume %s.", LOG.exception("Failed to delete volume %s.",
volmap.cinder_volume_id) volmap.cinder_volume_id)
@ -452,18 +469,18 @@ class Manager(periodic_task.PeriodicTasks):
for volmap in volmaps: for volmap in volmaps:
db_volmaps = objects.VolumeMapping.list_by_cinder_volume( db_volmaps = objects.VolumeMapping.list_by_cinder_volume(
context, volmap.cinder_volume_id) context, volmap.cinder_volume_id)
self._detach_volume(context, volmap, reraise=reraise) self._detach_volume(context, container, volmap, reraise=reraise)
if volmap.auto_remove and len(db_volmaps) == 1: if volmap.auto_remove and len(db_volmaps) == 1:
self.driver.delete_volume(context, volmap) self._get_driver(container).delete_volume(context, volmap)
auto_remove_volmaps.append(volmap) auto_remove_volmaps.append(volmap)
self._wait_for_volumes_deleted(context, auto_remove_volmaps, container) self._wait_for_volumes_deleted(context, auto_remove_volmaps, container)
def _detach_volume(self, context, volmap, reraise=True): def _detach_volume(self, context, container, volmap, reraise=True):
if objects.VolumeMapping.count( if objects.VolumeMapping.count(
context, volume_id=volmap.volume_id) == 1: context, volume_id=volmap.volume_id) == 1:
context = context.elevated() context = context.elevated()
try: try:
self.driver.detach_volume(context, volmap) self._get_driver(container).detach_volume(context, volmap)
except Exception: except Exception:
with excutils.save_and_reraise_exception(reraise=reraise): with excutils.save_and_reraise_exception(reraise=reraise):
LOG.error("Failed to detach volume %(volume_id)s from " LOG.error("Failed to detach volume %(volume_id)s from "
@ -478,6 +495,7 @@ class Manager(periodic_task.PeriodicTasks):
with self._update_task_state(context, container, with self._update_task_state(context, container,
consts.CONTAINER_STARTING): consts.CONTAINER_STARTING):
try: try:
# NOTE(hongbin): capsule shouldn't reach here
container = self.driver.start(context, container) container = self.driver.start(context, container)
container.started_at = timeutils.utcnow() container.started_at = timeutils.utcnow()
container.save(context) container.save(context)
@ -508,7 +526,8 @@ class Manager(periodic_task.PeriodicTasks):
reraise = not force reraise = not force
try: try:
if isinstance(container, objects.Capsule): if isinstance(container, objects.Capsule):
self.driver.delete_capsule(context, container, force) self.capsule_driver.delete_capsule(context, container,
force)
elif isinstance(container, objects.Container): elif isinstance(container, objects.Container):
self.driver.delete(context, container, force) self.driver.delete(context, container, force)
except exception.DockerError as e: except exception.DockerError as e:
@ -546,6 +565,7 @@ class Manager(periodic_task.PeriodicTasks):
def _add_security_group(self, context, container, security_group): def _add_security_group(self, context, container, security_group):
LOG.debug('Adding security_group to container: %s', container.uuid) LOG.debug('Adding security_group to container: %s', container.uuid)
with self._update_task_state(context, container, consts.SG_ADDING): with self._update_task_state(context, container, consts.SG_ADDING):
# NOTE(hongbin): capsule shouldn't reach here
self.driver.add_security_group(context, container, security_group) self.driver.add_security_group(context, container, security_group)
container.security_groups += [security_group] container.security_groups += [security_group]
container.save(context) container.save(context)
@ -564,6 +584,7 @@ class Manager(periodic_task.PeriodicTasks):
def _remove_security_group(self, context, container, security_group): def _remove_security_group(self, context, container, security_group):
LOG.debug('Removing security_group from container: %s', container.uuid) LOG.debug('Removing security_group from container: %s', container.uuid)
with self._update_task_state(context, container, consts.SG_REMOVING): with self._update_task_state(context, container, consts.SG_REMOVING):
# NOTE(hongbin): capsule shouldn't reach here
self.driver.remove_security_group(context, container, self.driver.remove_security_group(context, container,
security_group) security_group)
container.security_groups = list(set(container.security_groups) container.security_groups = list(set(container.security_groups)
@ -574,6 +595,7 @@ class Manager(periodic_task.PeriodicTasks):
def container_show(self, context, container): def container_show(self, context, container):
LOG.debug('Showing container: %s', container.uuid) LOG.debug('Showing container: %s', container.uuid)
try: try:
# NOTE(hongbin): capsule shouldn't reach here
container = self.driver.show(context, container) container = self.driver.show(context, container)
if container.obj_what_changed(): if container.obj_what_changed():
container.save(context) container.save(context)
@ -593,6 +615,7 @@ class Manager(periodic_task.PeriodicTasks):
LOG.debug('Rebooting container: %s', container.uuid) LOG.debug('Rebooting container: %s', container.uuid)
with self._update_task_state(context, container, with self._update_task_state(context, container,
consts.CONTAINER_REBOOTING): consts.CONTAINER_REBOOTING):
# NOTE(hongbin): capsule shouldn't reach here
container = self.driver.reboot(context, container, timeout) container = self.driver.reboot(context, container, timeout)
return container return container
@ -610,6 +633,7 @@ class Manager(periodic_task.PeriodicTasks):
LOG.debug('Stopping container: %s', container.uuid) LOG.debug('Stopping container: %s', container.uuid)
with self._update_task_state(context, container, with self._update_task_state(context, container,
consts.CONTAINER_STOPPING): consts.CONTAINER_STOPPING):
# NOTE(hongbin): capsule shouldn't reach here
container = self.driver.stop(context, container, timeout) container = self.driver.stop(context, container, timeout)
return container return container
@ -644,16 +668,15 @@ class Manager(periodic_task.PeriodicTasks):
except Exception as e: except Exception as e:
with excutils.save_and_reraise_exception(): with excutils.save_and_reraise_exception():
self._fail_container(context, container, six.text_type(e)) self._fail_container(context, container, six.text_type(e))
# NOTE(hongbin): capsule shouldn't reach here
if self.driver.check_container_exist(container): if self.driver.check_container_exist(container):
for addr in container.addresses.values(): for addr in container.addresses.values():
for port in addr: for port in addr:
port['preserve_on_delete'] = True port['preserve_on_delete'] = True
try: try:
if isinstance(container, objects.Capsule): # NOTE(hongbin): capsule shouldn't reach here
self.driver.delete_capsule(context, container) self.driver.delete(context, container, True)
elif isinstance(container, objects.Container):
self.driver.delete(context, container, True)
except Exception as e: except Exception as e:
with excutils.save_and_reraise_exception(): with excutils.save_and_reraise_exception():
LOG.error("Rebuild container: %s failed, " LOG.error("Rebuild container: %s failed, "
@ -729,6 +752,7 @@ class Manager(periodic_task.PeriodicTasks):
LOG.debug('Pausing container: %s', container.uuid) LOG.debug('Pausing container: %s', container.uuid)
with self._update_task_state(context, container, with self._update_task_state(context, container,
consts.CONTAINER_PAUSING): consts.CONTAINER_PAUSING):
# NOTE(hongbin): capsule shouldn't reach here
container = self.driver.pause(context, container) container = self.driver.pause(context, container)
return container return container
@ -746,6 +770,7 @@ class Manager(periodic_task.PeriodicTasks):
LOG.debug('Unpausing container: %s', container.uuid) LOG.debug('Unpausing container: %s', container.uuid)
with self._update_task_state(context, container, with self._update_task_state(context, container,
consts.CONTAINER_UNPAUSING): consts.CONTAINER_UNPAUSING):
# NOTE(hongbin): capsule shouldn't reach here
container = self.driver.unpause(context, container) container = self.driver.unpause(context, container)
return container return container
@ -761,6 +786,7 @@ class Manager(periodic_task.PeriodicTasks):
timestamps, tail, since): timestamps, tail, since):
LOG.debug('Showing container logs: %s', container.uuid) LOG.debug('Showing container logs: %s', container.uuid)
try: try:
# NOTE(hongbin): capsule shouldn't reach here
return self.driver.show_logs(context, container, return self.driver.show_logs(context, container,
stdout=stdout, stderr=stderr, stdout=stdout, stderr=stderr,
timestamps=timestamps, tail=tail, timestamps=timestamps, tail=tail,
@ -777,9 +803,11 @@ class Manager(periodic_task.PeriodicTasks):
def container_exec(self, context, container, command, run, interactive): def container_exec(self, context, container, command, run, interactive):
LOG.debug('Executing command in container: %s', container.uuid) LOG.debug('Executing command in container: %s', container.uuid)
try: try:
# NOTE(hongbin): capsule shouldn't reach here
exec_id = self.driver.execute_create(context, container, command, exec_id = self.driver.execute_create(context, container, command,
interactive) interactive)
if run: if run:
# NOTE(hongbin): capsule shouldn't reach here
output, exit_code = self.driver.execute_run(exec_id, command) output, exit_code = self.driver.execute_run(exec_id, command)
return {"output": output, return {"output": output,
"exit_code": exit_code, "exit_code": exit_code,
@ -808,6 +836,7 @@ class Manager(periodic_task.PeriodicTasks):
def container_exec_resize(self, context, exec_id, height, width): def container_exec_resize(self, context, exec_id, height, width):
LOG.debug('Resizing the tty session used by the exec: %s', exec_id) LOG.debug('Resizing the tty session used by the exec: %s', exec_id)
try: try:
# NOTE(hongbin): capsule shouldn't reach here
return self.driver.execute_resize(exec_id, height, width) return self.driver.execute_resize(exec_id, height, width)
except exception.DockerError as e: except exception.DockerError as e:
LOG.error("Error occurred while calling Docker exec API: %s", LOG.error("Error occurred while calling Docker exec API: %s",
@ -824,6 +853,7 @@ class Manager(periodic_task.PeriodicTasks):
LOG.debug('Killing a container: %s', container.uuid) LOG.debug('Killing a container: %s', container.uuid)
with self._update_task_state(context, container, with self._update_task_state(context, container,
consts.CONTAINER_KILLING): consts.CONTAINER_KILLING):
# NOTE(hongbin): capsule shouldn't reach here
container = self.driver.kill(context, container, signal) container = self.driver.kill(context, container, signal)
return container return container
@ -854,6 +884,7 @@ class Manager(periodic_task.PeriodicTasks):
container) container)
with rt.container_update_claim(context, container, old_container, with rt.container_update_claim(context, container, old_container,
limits): limits):
# NOTE(hongbin): capsule shouldn't reach here
self.driver.update(context, container) self.driver.update(context, container)
container.save(context) container.save(context)
return container return container
@ -870,6 +901,7 @@ class Manager(periodic_task.PeriodicTasks):
def container_attach(self, context, container): def container_attach(self, context, container):
LOG.debug('Get websocket url from the container: %s', container.uuid) LOG.debug('Get websocket url from the container: %s', container.uuid)
try: try:
# NOTE(hongbin): capsule shouldn't reach here
url = self.driver.get_websocket_url(context, container) url = self.driver.get_websocket_url(context, container)
token = uuidutils.generate_uuid() token = uuidutils.generate_uuid()
container.websocket_url = url container.websocket_url = url
@ -886,6 +918,7 @@ class Manager(periodic_task.PeriodicTasks):
def container_resize(self, context, container, height, width): def container_resize(self, context, container, height, width):
LOG.debug('Resize tty to the container: %s', container.uuid) LOG.debug('Resize tty to the container: %s', container.uuid)
try: try:
# NOTE(hongbin): capsule shouldn't reach here
container = self.driver.resize(context, container, height, width) container = self.driver.resize(context, container, height, width)
return container return container
except exception.DockerError as e: except exception.DockerError as e:
@ -899,6 +932,7 @@ class Manager(periodic_task.PeriodicTasks):
LOG.debug('Displaying the running processes inside the container: %s', LOG.debug('Displaying the running processes inside the container: %s',
container.uuid) container.uuid)
try: try:
# NOTE(hongbin): capsule shouldn't reach here
return self.driver.top(context, container, ps_args) return self.driver.top(context, container, ps_args)
except exception.DockerError as e: except exception.DockerError as e:
LOG.error("Error occurred while calling Docker top API: %s", LOG.error("Error occurred while calling Docker top API: %s",
@ -912,6 +946,7 @@ class Manager(periodic_task.PeriodicTasks):
def container_get_archive(self, context, container, path, encode_data): def container_get_archive(self, context, container, path, encode_data):
LOG.debug('Copying resource from the container: %s', container.uuid) LOG.debug('Copying resource from the container: %s', container.uuid)
try: try:
# NOTE(hongbin): capsule shouldn't reach here
filedata, stat = self.driver.get_archive(context, container, path) filedata, stat = self.driver.get_archive(context, container, path)
if encode_data: if encode_data:
filedata = utils.encode_file_data(filedata) filedata = utils.encode_file_data(filedata)
@ -932,6 +967,7 @@ class Manager(periodic_task.PeriodicTasks):
if decode_data: if decode_data:
data = utils.decode_file_data(data) data = utils.decode_file_data(data)
try: try:
# NOTE(hongbin): capsule shouldn't reach here
return self.driver.put_archive(context, container, path, data) return self.driver.put_archive(context, container, path, data)
except exception.DockerError as e: except exception.DockerError as e:
LOG.error( LOG.error(
@ -946,6 +982,7 @@ class Manager(periodic_task.PeriodicTasks):
def container_stats(self, context, container): def container_stats(self, context, container):
LOG.debug('Displaying stats of the container: %s', container.uuid) LOG.debug('Displaying stats of the container: %s', container.uuid)
try: try:
# NOTE(hongbin): capsule shouldn't reach here
return self.driver.stats(context, container) return self.driver.stats(context, container)
except exception.DockerError as e: except exception.DockerError as e:
LOG.error("Error occurred while calling Docker stats API: %s", LOG.error("Error occurred while calling Docker stats API: %s",
@ -963,6 +1000,7 @@ class Manager(periodic_task.PeriodicTasks):
# NOTE(miaohb): Glance is the only driver that support image # NOTE(miaohb): Glance is the only driver that support image
# uploading in the current version, so we have hard-coded here. # uploading in the current version, so we have hard-coded here.
# https://bugs.launchpad.net/zun/+bug/1697342 # https://bugs.launchpad.net/zun/+bug/1697342
# NOTE(hongbin): capsule shouldn't reach here
snapshot_image = self.driver.create_image(context, repository, snapshot_image = self.driver.create_image(context, repository,
glance.GlanceDriver()) glance.GlanceDriver())
except exception.DockerError as e: except exception.DockerError as e:
@ -981,11 +1019,13 @@ class Manager(periodic_task.PeriodicTasks):
def _do_container_image_upload(self, context, snapshot_image, def _do_container_image_upload(self, context, snapshot_image,
container_image_id, data, tag): container_image_id, data, tag):
try: try:
# NOTE(hongbin): capsule shouldn't reach here
self.driver.upload_image_data(context, snapshot_image, self.driver.upload_image_data(context, snapshot_image,
tag, data, glance.GlanceDriver()) tag, data, glance.GlanceDriver())
except Exception as e: except Exception as e:
LOG.exception("Unexpected exception while uploading image: %s", LOG.exception("Unexpected exception while uploading image: %s",
six.text_type(e)) six.text_type(e))
# NOTE(hongbin): capsule shouldn't reach here
self.driver.delete_committed_image(context, snapshot_image.id, self.driver.delete_committed_image(context, snapshot_image.id,
glance.GlanceDriver()) glance.GlanceDriver())
self.driver.delete_image(context, container_image_id, self.driver.delete_image(context, container_image_id,
@ -1004,23 +1044,27 @@ class Manager(periodic_task.PeriodicTasks):
# ensure the container is paused before doing commit # ensure the container is paused before doing commit
unpause = False unpause = False
if container.status == consts.RUNNING: if container.status == consts.RUNNING:
# NOTE(hongbin): capsule shouldn't reach here
container = self.driver.pause(context, container) container = self.driver.pause(context, container)
container.save(context) container.save(context)
unpause = True unpause = True
try: try:
# NOTE(hongbin): capsule shouldn't reach here
container_image_id = self.driver.commit(context, container, container_image_id = self.driver.commit(context, container,
repository, tag) repository, tag)
container_image = self.driver.get_image(repository + ':' + tag) container_image = self.driver.get_image(repository + ':' + tag)
except exception.DockerError as e: except exception.DockerError as e:
LOG.error("Error occurred while calling docker commit API: %s", LOG.error("Error occurred while calling docker commit API: %s",
six.text_type(e)) six.text_type(e))
# NOTE(hongbin): capsule shouldn't reach here
self.driver.delete_committed_image(context, snapshot_image.id, self.driver.delete_committed_image(context, snapshot_image.id,
glance.GlanceDriver()) glance.GlanceDriver())
raise raise
finally: finally:
if unpause: if unpause:
try: try:
# NOTE(hongbin): capsule shouldn't reach here
container = self.driver.unpause(context, container) container = self.driver.unpause(context, container)
container.save(context) container.save(context)
except Exception as e: except Exception as e:
@ -1125,7 +1169,7 @@ class Manager(periodic_task.PeriodicTasks):
def _get_resource_tracker(self): def _get_resource_tracker(self):
if not self._resource_tracker: if not self._resource_tracker:
rt = compute_node_tracker.ComputeNodeTracker( rt = compute_node_tracker.ComputeNodeTracker(
self.host, self.driver, self.reportclient) self.host, self.driver, self.capsule_driver, self.reportclient)
self._resource_tracker = rt self._resource_tracker = rt
return self._resource_tracker return self._resource_tracker
@ -1168,6 +1212,7 @@ class Manager(periodic_task.PeriodicTasks):
containers = objects.Container.list(ctx) containers = objects.Container.list(ctx)
self.driver.update_containers_states(ctx, containers, self) self.driver.update_containers_states(ctx, containers, self)
capsules = objects.Capsule.list(ctx) capsules = objects.Capsule.list(ctx)
# TODO(hongbin): use capsule driver to update capsules status
self.driver.update_containers_states(ctx, capsules, self) self.driver.update_containers_states(ctx, capsules, self)
def network_detach(self, context, container, network): def network_detach(self, context, container, network):
@ -1185,6 +1230,7 @@ class Manager(periodic_task.PeriodicTasks):
{'container': container, 'network': network}) {'container': container, 'network': network})
with self._update_task_state(context, container, with self._update_task_state(context, container,
consts.NETWORK_DETACHING): consts.NETWORK_DETACHING):
# NOTE(hongbin): capsule shouldn't reach here
self.driver.network_detach(context, container, network) self.driver.network_detach(context, container, network)
def network_attach(self, context, container, requested_network): def network_attach(self, context, container, requested_network):
@ -1202,6 +1248,7 @@ class Manager(periodic_task.PeriodicTasks):
{'container': container, 'network': requested_network}) {'container': container, 'network': requested_network})
with self._update_task_state(context, container, with self._update_task_state(context, container,
consts.NETWORK_ATTACHING): consts.NETWORK_ATTACHING):
# NOTE(hongbin): capsule shouldn't reach here
self.driver.network_attach(context, container, requested_network) self.driver.network_attach(context, container, requested_network)
def network_create(self, context, neutron_net_id): def network_create(self, context, neutron_net_id):

View File

@ -27,6 +27,21 @@ Services which consume this:
Interdependencies to other options: Interdependencies to other options:
* None
"""),
cfg.StrOpt('capsule_driver',
default='docker',
help="""Defines which driver to use for controlling capsule.
Possible values:
* ``docker``
Services which consume this:
* ``zun-compute``
Interdependencies to other options:
* None * None
"""), """),
cfg.IntOpt('default_sleep_time', default=1, cfg.IntOpt('default_sleep_time', default=1,

View File

@ -104,7 +104,8 @@ def wrap_docker_error(function):
return decorated_function return decorated_function
class DockerDriver(driver.BaseDriver, driver.ContainerDriver): class DockerDriver(driver.BaseDriver, driver.ContainerDriver,
driver.CapsuleDriver):
"""Implementation of container drivers for Docker.""" """Implementation of container drivers for Docker."""
# TODO(hongbin): define a list of capabilities of this driver. # TODO(hongbin): define a list of capabilities of this driver.
@ -1323,6 +1324,7 @@ class DockerDriver(driver.BaseDriver, driver.ContainerDriver):
for container in capsule.containers: for container in capsule.containers:
self._delete_container_in_capsule(context, capsule, container, self._delete_container_in_capsule(context, capsule, container,
force) force)
self.stop(context, capsule, None)
self.delete(context, capsule, force) self.delete(context, capsule, force)
def _delete_container_in_capsule(self, context, capsule, container, force): def _delete_container_in_capsule(self, context, capsule, container, force):

View File

@ -83,6 +83,19 @@ def load_container_driver(container_driver=None):
sys.exit(1) sys.exit(1)
def load_capsule_driver():
driver = stevedore_driver.DriverManager(
"zun.capsule.driver",
CONF.capsule_driver,
invoke_on_load=True).driver
if not isinstance(driver, CapsuleDriver):
raise Exception(_('Expected driver of type: %s') %
str(ContainerDriver))
return driver
class BaseDriver(object): class BaseDriver(object):
"""Base class for driver.""" """Base class for driver."""
@ -146,6 +159,9 @@ class BaseDriver(object):
def node_support_disk_quota(self): def node_support_disk_quota(self):
return False return False
def get_host_default_base_size(self):
return None
def get_available_resources(self): def get_available_resources(self):
"""Retrieve resource information. """Retrieve resource information.
@ -434,9 +450,6 @@ class ContainerDriver(object):
def inspect_network(self, network): def inspect_network(self, network):
raise NotImplementedError() raise NotImplementedError()
def get_host_default_base_size(self):
raise NotImplementedError()
def pull_image(self, context, repo, tag, **kwargs): def pull_image(self, context, repo, tag, **kwargs):
raise NotImplementedError() raise NotImplementedError()
@ -456,6 +469,10 @@ class ContainerDriver(object):
def delete_image(self, context, img_id, image_driver): def delete_image(self, context, img_id, image_driver):
raise NotImplementedError() raise NotImplementedError()
class CapsuleDriver(object):
"""Interface for container driver."""
def create_capsule(self, context, capsule, **kwargs): def create_capsule(self, context, capsule, **kwargs):
raise NotImplementedError() raise NotImplementedError()

View File

@ -498,12 +498,10 @@ class TestCapsuleController(api_base.FunctionalTest):
response.json['uuid']) response.json['uuid'])
@patch('zun.compute.api.API.container_delete') @patch('zun.compute.api.API.container_delete')
@patch('zun.compute.api.API.container_stop')
@patch('zun.objects.Capsule.get_by_uuid') @patch('zun.objects.Capsule.get_by_uuid')
@patch('zun.objects.Capsule.save') @patch('zun.objects.Capsule.save')
def test_delete_capsule_by_uuid(self, mock_capsule_save, def test_delete_capsule_by_uuid(self, mock_capsule_save,
mock_capsule_get_by_uuid, mock_capsule_get_by_uuid,
mock_capsule_stop,
mock_capsule_delete): mock_capsule_delete):
test_capsule = utils.create_test_container(context=self.context) test_capsule = utils.create_test_container(context=self.context)
test_capsule_obj = objects.Capsule(self.context, test_capsule_obj = objects.Capsule(self.context,
@ -516,20 +514,17 @@ class TestCapsuleController(api_base.FunctionalTest):
response = self.app.delete('/v1/capsules/%s' % capsule_uuid) response = self.app.delete('/v1/capsules/%s' % capsule_uuid)
self.assertTrue(mock_capsule_delete.called) self.assertTrue(mock_capsule_delete.called)
self.assertTrue(mock_capsule_stop.called)
self.assertEqual(204, response.status_int) self.assertEqual(204, response.status_int)
context = mock_capsule_save.call_args[0][0] context = mock_capsule_save.call_args[0][0]
self.assertIs(False, context.all_projects) self.assertIs(False, context.all_projects)
@patch('zun.common.policy.enforce') @patch('zun.common.policy.enforce')
@patch('zun.compute.api.API.container_delete') @patch('zun.compute.api.API.container_delete')
@patch('zun.compute.api.API.container_stop')
@patch('zun.objects.Capsule.get_by_uuid') @patch('zun.objects.Capsule.get_by_uuid')
@patch('zun.objects.Capsule.save') @patch('zun.objects.Capsule.save')
def test_delete_capsule_by_uuid_all_projects(self, def test_delete_capsule_by_uuid_all_projects(self,
mock_capsule_save, mock_capsule_save,
mock_capsule_get_by_uuid, mock_capsule_get_by_uuid,
mock_capsule_stop,
mock_capsule_delete, mock_capsule_delete,
mock_policy): mock_policy):
mock_policy.return_value = True mock_policy.return_value = True
@ -545,7 +540,6 @@ class TestCapsuleController(api_base.FunctionalTest):
'/v1/capsules/%s/?all_projects=1' % capsule_uuid) '/v1/capsules/%s/?all_projects=1' % capsule_uuid)
self.assertTrue(mock_capsule_delete.called) self.assertTrue(mock_capsule_delete.called)
self.assertTrue(mock_capsule_stop.called)
self.assertEqual(204, response.status_int) self.assertEqual(204, response.status_int)
context = mock_capsule_save.call_args[0][0] context = mock_capsule_save.call_args[0][0]
self.assertIs(True, context.all_projects) self.assertIs(True, context.all_projects)
@ -556,12 +550,10 @@ class TestCapsuleController(api_base.FunctionalTest):
'/capsules/%s' % uuid) '/capsules/%s' % uuid)
@patch('zun.compute.api.API.container_delete') @patch('zun.compute.api.API.container_delete')
@patch('zun.compute.api.API.container_stop')
@patch('zun.objects.Capsule.get_by_name') @patch('zun.objects.Capsule.get_by_name')
@patch('zun.objects.Capsule.save') @patch('zun.objects.Capsule.save')
def test_delete_capsule_by_name(self, mock_capsule_save, def test_delete_capsule_by_name(self, mock_capsule_save,
mock_capsule_get_by_uuid, mock_capsule_get_by_uuid,
mock_capsule_stop,
mock_capsule_delete): mock_capsule_delete):
test_capsule = utils.create_test_container(context=self.context) test_capsule = utils.create_test_container(context=self.context)
test_capsule_obj = objects.Capsule(self.context, test_capsule_obj = objects.Capsule(self.context,
@ -575,7 +567,6 @@ class TestCapsuleController(api_base.FunctionalTest):
capsule_name) capsule_name)
self.assertTrue(mock_capsule_delete.called) self.assertTrue(mock_capsule_delete.called)
self.assertTrue(mock_capsule_stop.called)
self.assertEqual(204, response.status_int) self.assertEqual(204, response.status_int)
context = mock_capsule_save.call_args[0][0] context = mock_capsule_save.call_args[0][0]
self.assertIs(False, context.all_projects) self.assertIs(False, context.all_projects)

View File

@ -100,6 +100,7 @@ class TestManager(base.TestCase):
self.addCleanup(p.stop) self.addCleanup(p.stop)
zun.conf.CONF.set_override('container_driver', 'fake') zun.conf.CONF.set_override('container_driver', 'fake')
zun.conf.CONF.set_override('capsule_driver', 'fake')
self.compute_manager = manager.Manager() self.compute_manager = manager.Manager()
self.compute_manager._resource_tracker = FakeResourceTracker() self.compute_manager._resource_tracker = FakeResourceTracker()

View File

@ -27,9 +27,11 @@ class TestNodeStracker(base.TestCase):
def setUp(self): def setUp(self):
super(TestNodeStracker, self).setUp() super(TestNodeStracker, self).setUp()
self.container_driver = fake_driver.FakeDriver() self.container_driver = fake_driver.FakeDriver()
self.capsule_driver = fake_driver.FakeDriver()
self.report_client_mock = mock.MagicMock() self.report_client_mock = mock.MagicMock()
self._resource_tracker = compute_node_tracker.ComputeNodeTracker( self._resource_tracker = compute_node_tracker.ComputeNodeTracker(
'testhost', self.container_driver, self.report_client_mock) 'testhost', self.container_driver, self.capsule_driver,
self.report_client_mock)
@mock.patch.object(compute_node_tracker.ComputeNodeTracker, '_update') @mock.patch.object(compute_node_tracker.ComputeNodeTracker, '_update')
@mock.patch.object(compute_node_tracker.ComputeNodeTracker, @mock.patch.object(compute_node_tracker.ComputeNodeTracker,

View File

@ -16,7 +16,8 @@ from zun.common.utils import check_container_id
from zun.container import driver from zun.container import driver
class FakeDriver(driver.BaseDriver, driver.ContainerDriver): class FakeDriver(driver.BaseDriver, driver.ContainerDriver,
driver.CapsuleDriver):
"""Fake driver for testing.""" """Fake driver for testing."""
def __init__(self): def __init__(self):