Merge "Add Commands For Docker Registry Image Cleanup"
This commit is contained in:
@@ -1,2 +1,2 @@
|
||||
SRC_DIR="cgts-client"
|
||||
TIS_PATCH_VER=64
|
||||
TIS_PATCH_VER=65
|
||||
|
||||
@@ -62,6 +62,7 @@ from cgtsclient.v1 import partition
|
||||
from cgtsclient.v1 import pci_device
|
||||
from cgtsclient.v1 import port
|
||||
from cgtsclient.v1 import ptp
|
||||
from cgtsclient.v1 import registry_image
|
||||
from cgtsclient.v1 import remotelogging
|
||||
from cgtsclient.v1 import route
|
||||
from cgtsclient.v1 import sdn_controller
|
||||
@@ -143,6 +144,7 @@ class Client(http.HTTPClient):
|
||||
self.sm_service = sm_service.SmServiceManager(self)
|
||||
self.sm_servicegroup = sm_servicegroup.SmServiceGroupManager(self)
|
||||
self.health = health.HealthManager(self)
|
||||
self.registry_image = registry_image.RegistryImageManager(self)
|
||||
self.remotelogging = remotelogging.RemoteLoggingManager(self)
|
||||
self.sdn_controller = sdn_controller.SDNControllerManager(self)
|
||||
self.partition = partition.partitionManager(self)
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
#
|
||||
# Copyright (c) 2019 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# -*- encoding: utf-8 -*-
|
||||
#
|
||||
|
||||
from cgtsclient.common import base
|
||||
from cgtsclient.v1 import options
|
||||
|
||||
|
||||
class RegistryImage(base.Resource):
|
||||
def __repr__(self):
|
||||
return "<registry_image %s>" % self._info
|
||||
|
||||
|
||||
class RegistryImageManager(base.Manager):
|
||||
resource_class = RegistryImage
|
||||
|
||||
@staticmethod
|
||||
def _path(name=None):
|
||||
return '/v1/registry_image/%s' % name if name else '/v1/registry_image'
|
||||
|
||||
def list(self):
|
||||
"""Retrieve the list of images from the registry."""
|
||||
|
||||
return self._list(self._path(), 'registry_images')
|
||||
|
||||
def tags(self, image_name):
|
||||
"""Retrieve the list of tags from the registry for a specified image.
|
||||
|
||||
:param image_name: image name
|
||||
"""
|
||||
path = options.build_url(self._path(), None, ['image_name=%s' % image_name])
|
||||
return self._list(path, 'registry_images')
|
||||
|
||||
def delete(self, image_name_and_tag):
|
||||
"""Delete registry image given name and tag
|
||||
|
||||
:param image_name_and_tag: a string of the form name:tag
|
||||
"""
|
||||
path = options.build_url(self._path(), None, ['image_name_and_tag=%s' % image_name_and_tag])
|
||||
return self._delete(path)
|
||||
|
||||
def garbage_collect(self):
|
||||
path = options.build_url(self._path(), None, ['garbage_collect=%s' % True])
|
||||
return self._create(path, {})
|
||||
@@ -0,0 +1,46 @@
|
||||
#!/usr/bin/env python
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
#
|
||||
# Copyright (c) 2019 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
from cgtsclient.common import utils
|
||||
from cgtsclient import exc
|
||||
|
||||
|
||||
def do_registry_image_list(cc, args):
|
||||
"""List all images in local docker registry"""
|
||||
images = cc.registry_image.list()
|
||||
labels = ['Image Name']
|
||||
fields = ['name']
|
||||
utils.print_list(images, fields, labels, sortby=0)
|
||||
|
||||
|
||||
@utils.arg('name', metavar='<image name>',
|
||||
help="Name of an image")
|
||||
def do_registry_image_tags(cc, args):
|
||||
"""List all tags for a Docker image from the local registry"""
|
||||
images = cc.registry_image.tags(args.name)
|
||||
labels = ['Image Tag']
|
||||
fields = ['tag']
|
||||
utils.print_list(images, fields, labels, sortby=0)
|
||||
|
||||
|
||||
@utils.arg('name_and_tag', metavar='<image name and tag>',
|
||||
help="Name and tag of an image, in the form name:tag")
|
||||
def do_registry_image_delete(cc, args):
|
||||
"""Remove the specified Docker image from the local registry"""
|
||||
try:
|
||||
cc.registry_image.delete(args.name_and_tag)
|
||||
print('Image %s deleted, please run garbage collect to free disk space.' % args.name_and_tag)
|
||||
except exc.HTTPNotFound:
|
||||
raise exc.CommandError('Image not found: %s' % args.name_and_tag)
|
||||
|
||||
|
||||
def do_registry_garbage_collect(cc, args):
|
||||
"""Run the registry garbage collector"""
|
||||
|
||||
cc.registry_image.garbage_collect()
|
||||
print('Running docker registry garbage collect')
|
||||
@@ -50,6 +50,7 @@ from cgtsclient.v1 import partition_shell
|
||||
from cgtsclient.v1 import pci_device_shell
|
||||
from cgtsclient.v1 import port_shell
|
||||
from cgtsclient.v1 import ptp_shell
|
||||
from cgtsclient.v1 import registry_image_shell
|
||||
from cgtsclient.v1 import remotelogging_shell
|
||||
from cgtsclient.v1 import route_shell
|
||||
from cgtsclient.v1 import sdn_controller_shell
|
||||
@@ -106,6 +107,7 @@ COMMAND_MODULES = [
|
||||
lldp_agent_shell,
|
||||
lldp_neighbour_shell,
|
||||
health_shell,
|
||||
registry_image_shell,
|
||||
remotelogging_shell,
|
||||
sdn_controller_shell,
|
||||
partition_shell,
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
SRC_DIR="sysinv"
|
||||
TIS_PATCH_VER=317
|
||||
TIS_PATCH_VER=318
|
||||
|
||||
@@ -59,6 +59,7 @@ from sysinv.api.controllers.v1 import port
|
||||
from sysinv.api.controllers.v1 import profile
|
||||
from sysinv.api.controllers.v1 import ptp
|
||||
from sysinv.api.controllers.v1 import pv
|
||||
from sysinv.api.controllers.v1 import registry_image
|
||||
from sysinv.api.controllers.v1 import remotelogging
|
||||
from sysinv.api.controllers.v1 import route
|
||||
from sysinv.api.controllers.v1 import sdn_controller
|
||||
@@ -223,6 +224,9 @@ class V1(base.APIBase):
|
||||
health = [link.Link]
|
||||
"Links to the system health resource"
|
||||
|
||||
registry_image = [link.Link]
|
||||
"Links to the Docker registry image resource"
|
||||
|
||||
remotelogging = [link.Link]
|
||||
"Links to the remotelogging resource"
|
||||
|
||||
@@ -686,6 +690,15 @@ class V1(base.APIBase):
|
||||
'health', '', bookmark=True)
|
||||
]
|
||||
|
||||
v1.registry_image = [link.Link.make_link('self',
|
||||
pecan.request.host_url,
|
||||
'registry_image', ''),
|
||||
link.Link.make_link('bookmark',
|
||||
pecan.request.host_url,
|
||||
'registry_image', '',
|
||||
bookmark=True)
|
||||
]
|
||||
|
||||
v1.remotelogging = [link.Link.make_link('self',
|
||||
pecan.request.host_url,
|
||||
'remotelogging', ''),
|
||||
@@ -809,6 +822,7 @@ class Controller(rest.RestController):
|
||||
servicenodes = servicenode.SMServiceNodeController()
|
||||
servicegroup = servicegroup.SMServiceGroupController()
|
||||
health = health.HealthController()
|
||||
registry_image = registry_image.RegistryImageController()
|
||||
remotelogging = remotelogging.RemoteLoggingController()
|
||||
sdn_controller = sdn_controller.SDNControllerController()
|
||||
license = license.LicenseController()
|
||||
|
||||
101
sysinv/sysinv/sysinv/sysinv/api/controllers/v1/registry_image.py
Normal file
101
sysinv/sysinv/sysinv/sysinv/api/controllers/v1/registry_image.py
Normal file
@@ -0,0 +1,101 @@
|
||||
#
|
||||
# Copyright (c) 2019 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
import pecan
|
||||
from pecan import rest
|
||||
|
||||
import wsme
|
||||
from wsme import types as wtypes
|
||||
import wsmeext.pecan as wsme_pecan
|
||||
|
||||
from sysinv.api.controllers.v1 import base
|
||||
from sysinv.api.controllers.v1 import collection
|
||||
from sysinv.common import utils as cutils
|
||||
from oslo_log import log
|
||||
from sysinv.openstack.common.gettextutils import _
|
||||
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
LOCK_NAME = 'RegistryImageController'
|
||||
|
||||
|
||||
class RegistryImage(base.APIBase):
|
||||
"""API representation of a docker registry image"""
|
||||
|
||||
name = wtypes.text
|
||||
"The Docker image name"
|
||||
|
||||
tag = wtypes.text
|
||||
"The Docker image tag"
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self.fields = []
|
||||
|
||||
# set fields manually since Registry image comes from docker registry
|
||||
# and not sysinv database
|
||||
for fp in ['name', 'tag']:
|
||||
self.fields.append(fp)
|
||||
setattr(self, fp, kwargs.get(fp, None))
|
||||
|
||||
@classmethod
|
||||
def convert_with_links(cls, rpc_app, expand=True):
|
||||
app = RegistryImage(**rpc_app)
|
||||
if not expand:
|
||||
app.unset_fields_except(['name', 'tag'])
|
||||
|
||||
return app
|
||||
|
||||
|
||||
class RegistryImageCollection(collection.Collection):
|
||||
"""API representation of a collection of registry images."""
|
||||
|
||||
registry_images = [RegistryImage]
|
||||
"A list containing RegistryImage objects"
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self._type = 'registry_images'
|
||||
|
||||
@classmethod
|
||||
def convert_with_links(cls, rpc_apps, expand=False):
|
||||
collection = RegistryImageCollection()
|
||||
collection.registry_images = [RegistryImage.convert_with_links(n, expand)
|
||||
for n in rpc_apps]
|
||||
return collection
|
||||
|
||||
|
||||
class RegistryImageController(rest.RestController):
|
||||
"""REST controller for Docker registry image."""
|
||||
|
||||
@wsme_pecan.wsexpose(RegistryImageCollection, wtypes.text)
|
||||
def get_all(self, image_name=None):
|
||||
|
||||
# no image_name provided, list images
|
||||
if image_name is None:
|
||||
images = pecan.request.rpcapi.docker_registry_image_list(pecan.request.context)
|
||||
# image_name provided, list tags of provided image
|
||||
else:
|
||||
images = pecan.request.rpcapi.docker_registry_image_tags(pecan.request.context, image_name)
|
||||
return RegistryImageCollection.convert_with_links(images)
|
||||
|
||||
@cutils.synchronized(LOCK_NAME)
|
||||
@wsme_pecan.wsexpose(None, wtypes.text, status_code=204)
|
||||
def delete(self, image_name_and_tag):
|
||||
"""Delete the image with the given name
|
||||
|
||||
:param name: image name and tag of the form name:tag
|
||||
"""
|
||||
|
||||
if len(image_name_and_tag.split(":")) != 2:
|
||||
raise wsme.exc.ClientSideError(_("Image name and tag must be of form name:tag"))
|
||||
|
||||
return pecan.request.rpcapi.docker_registry_image_delete(pecan.request.context, image_name_and_tag)
|
||||
|
||||
@cutils.synchronized(LOCK_NAME)
|
||||
@wsme_pecan.wsexpose(None, wtypes.text)
|
||||
def post(self, garbage_collect=None):
|
||||
"""Run the registry garbage collector"""
|
||||
if garbage_collect is not None:
|
||||
pecan.request.rpcapi.docker_registry_garbage_collect(pecan.request.context)
|
||||
@@ -1314,6 +1314,7 @@ MURANO_CERT_KEY_FILE = os.path.join(CERT_MURANO_DIR, CERT_KEY_FILE)
|
||||
MURANO_CERT_FILE = os.path.join(CERT_MURANO_DIR, CERT_FILE)
|
||||
MURANO_CERT_CA_FILE = os.path.join(CERT_MURANO_DIR, CERT_CA_FILE)
|
||||
|
||||
DOCKER_REGISTRY_PORT = '9001'
|
||||
DOCKER_REGISTRY_CERT_FILE = os.path.join(SSL_CERT_DIR, "registry-cert.crt")
|
||||
DOCKER_REGISTRY_KEY_FILE = os.path.join(SSL_CERT_DIR, "registry-cert.key")
|
||||
DOCKER_REGISTRY_PKCS1_KEY_FILE = os.path.join(SSL_CERT_DIR,
|
||||
|
||||
105
sysinv/sysinv/sysinv/sysinv/conductor/docker_registry.py
Normal file
105
sysinv/sysinv/sysinv/sysinv/conductor/docker_registry.py
Normal file
@@ -0,0 +1,105 @@
|
||||
#
|
||||
# Copyright (c) 2019 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
import base64
|
||||
import keyring
|
||||
import requests
|
||||
|
||||
from sysinv.common import exception
|
||||
|
||||
CERT_PATH = '/etc/ssl/private/registry-cert.crt'
|
||||
KEYRING_SERVICE = 'CGCS'
|
||||
REGISTRY_USERNAME = 'admin'
|
||||
|
||||
|
||||
def get_registry_password():
|
||||
registry_password = keyring.get_password(
|
||||
KEYRING_SERVICE, REGISTRY_USERNAME)
|
||||
if not registry_password:
|
||||
raise exception.DockerRegistryCredentialNotFound(
|
||||
name=REGISTRY_USERNAME)
|
||||
return registry_password
|
||||
|
||||
|
||||
def docker_registry_authenticate(www_authenticate):
|
||||
"""
|
||||
returns a dictionary of headers to add as part of original request
|
||||
including access_token
|
||||
takes the Www-Authenticate header from the 401 response of a
|
||||
registry request
|
||||
like 'Bearer realm="https://192.168.204.2:9002/token/",
|
||||
service="192.168.204.2:9001",scope="registry:catalog:*"'
|
||||
|
||||
:param www_authenticate: a Www-Authenticate header as described above
|
||||
"""
|
||||
|
||||
# additional headers from the result of authentication
|
||||
# for example, access_token
|
||||
# send these along with the request to the docker registry
|
||||
auth_headers = {'connection': 'close'}
|
||||
|
||||
# take off the "Bearer"
|
||||
auth_params = www_authenticate.split(' ')
|
||||
# unsupported www_authenticate header
|
||||
if len(auth_params) != 2 or auth_params[0] != 'Bearer':
|
||||
return {}
|
||||
|
||||
auth_params = auth_params[1].split(',')
|
||||
# each auth_params should be an entry like
|
||||
# service="192.168.204.2:9001"
|
||||
for auth_param in auth_params:
|
||||
auth_param = auth_param.split('=')
|
||||
# we need to strip quotes from the auth challenge
|
||||
# if we send the "scope" field in quotes, we will get
|
||||
# "token intended for another audience" errors
|
||||
auth_headers[auth_param[0]] = auth_param[1].strip('\"')
|
||||
|
||||
# 'realm' specifies a token server to authenticate to
|
||||
if 'realm' not in auth_headers:
|
||||
return {}
|
||||
|
||||
# make a request to the token server
|
||||
# the credentials are passed as a header while the rest
|
||||
# are passed as params
|
||||
auth_string = base64.b64encode("%s:%s" % (REGISTRY_USERNAME, get_registry_password()))
|
||||
token_server_request_headers = {"authorization": "Basic %s" % auth_string}
|
||||
token_server_response = requests.get(auth_headers['realm'], verify=CERT_PATH,
|
||||
params=auth_headers,
|
||||
headers=token_server_request_headers)
|
||||
|
||||
if token_server_response.status_code == 200:
|
||||
auth_headers['Authorization'] = "Bearer %s" % token_server_response.json().get("access_token")
|
||||
|
||||
return auth_headers
|
||||
|
||||
|
||||
def docker_registry_get(path, registry_addr):
|
||||
# we need to have this header to get the correct digest when giving the tag
|
||||
headers = {"Accept": "application/vnd.docker.distribution.manifest.v2+json"}
|
||||
|
||||
resp = requests.get("%s%s" % (registry_addr, path), verify=CERT_PATH, headers=headers)
|
||||
|
||||
# authenticated registry, need to do auth with token server
|
||||
if resp.status_code == 401:
|
||||
auth_headers = docker_registry_authenticate(resp.headers["Www-Authenticate"])
|
||||
headers.update(auth_headers)
|
||||
resp = requests.get("%s%s" % (registry_addr, path), verify=CERT_PATH, headers=headers)
|
||||
|
||||
return resp
|
||||
|
||||
|
||||
def docker_registry_delete(path, registry_addr):
|
||||
headers = {}
|
||||
|
||||
resp = requests.delete("%s%s" % (registry_addr, path), verify=CERT_PATH, headers=headers)
|
||||
|
||||
# authenticated registry, need to do auth with token server
|
||||
if resp.status_code == 401:
|
||||
auth_headers = docker_registry_authenticate(resp.headers["Www-Authenticate"])
|
||||
headers.update(auth_headers)
|
||||
resp = requests.delete("%s%s" % (registry_addr, path), verify=CERT_PATH, headers=headers)
|
||||
|
||||
return resp
|
||||
@@ -64,7 +64,6 @@ TARFILE_TRANSFER_CHUNK_SIZE = 1024 * 512
|
||||
DOCKER_REGISTRY_USER = 'admin'
|
||||
DOCKER_REGISTRY_SERVICE = 'CGCS'
|
||||
DOCKER_REGISTRY_SECRET = 'default-registry-key'
|
||||
DOCKER_REGISTRY_PORT = '9001'
|
||||
|
||||
|
||||
# Helper functions
|
||||
@@ -1500,7 +1499,7 @@ class DockerHelper(object):
|
||||
cutils.format_address_name(constants.CONTROLLER_HOSTNAME,
|
||||
constants.NETWORK_TYPE_MGMT)
|
||||
).address
|
||||
registry_server = '{}:{}'.format(registry_ip, DOCKER_REGISTRY_PORT)
|
||||
registry_server = '{}:{}'.format(registry_ip, constants.DOCKER_REGISTRY_PORT)
|
||||
return registry_server
|
||||
|
||||
def _get_img_tag_with_registry(self, pub_img_tag):
|
||||
|
||||
@@ -85,6 +85,7 @@ from cephclient import wrapper as ceph
|
||||
from sysinv.conductor import ceph as iceph
|
||||
from sysinv.conductor import kube_app
|
||||
from sysinv.conductor import openstack
|
||||
from sysinv.conductor import docker_registry
|
||||
from sysinv.db import api as dbapi
|
||||
from sysinv.objects import base as objects_base
|
||||
from sysinv.objects import kube_app as kubeapp_obj
|
||||
@@ -1429,6 +1430,104 @@ class ConductorManager(service.PeriodicService):
|
||||
}
|
||||
self._config_apply_runtime_manifest(context, config_uuid, config_dict)
|
||||
|
||||
def _get_docker_registry_addr(self):
|
||||
registry_ip = self.dbapi.address_get_by_name(
|
||||
cutils.format_address_name(constants.CONTROLLER_HOSTNAME,
|
||||
constants.NETWORK_TYPE_MGMT)
|
||||
).address
|
||||
registry_server = 'https://{}:{}/v2/'.format(
|
||||
registry_ip, constants.DOCKER_REGISTRY_PORT)
|
||||
return registry_server
|
||||
|
||||
def docker_registry_image_list(self, context):
|
||||
image_list_response = docker_registry.docker_registry_get(
|
||||
"_catalog", self._get_docker_registry_addr())
|
||||
|
||||
if image_list_response.status_code != 200:
|
||||
LOG.error("Bad response from docker registry: %s"
|
||||
% image_list_response.status_code)
|
||||
return []
|
||||
|
||||
image_list_response = image_list_response.json()
|
||||
images = []
|
||||
# responses from the registry looks like this
|
||||
# {u'repositories': [u'meliodas/satesatesate', ...]}
|
||||
# we need to turn that into what we want to return:
|
||||
# [{'name': u'meliodas/satesatesate'}]
|
||||
if 'repositories' not in image_list_response:
|
||||
return images
|
||||
|
||||
image_list_response = image_list_response['repositories']
|
||||
for image in image_list_response:
|
||||
images.append({'name': image})
|
||||
|
||||
return images
|
||||
|
||||
def docker_registry_image_tags(self, context, image_name):
|
||||
image_tags_response = docker_registry.docker_registry_get(
|
||||
"%s/tags/list" % image_name, self._get_docker_registry_addr())
|
||||
|
||||
if image_tags_response.status_code != 200:
|
||||
LOG.error("Bad response from docker registry: %s"
|
||||
% image_tags_response.status_code)
|
||||
return []
|
||||
|
||||
image_tags_response = image_tags_response.json()
|
||||
tags = []
|
||||
|
||||
if 'tags' not in image_tags_response:
|
||||
return tags
|
||||
|
||||
image_tags_response = image_tags_response['tags']
|
||||
# in the case where all tags of an image is deleted but not
|
||||
# garbage collected
|
||||
# the response will contain "tags:null"
|
||||
if image_tags_response is not None:
|
||||
for tag in image_tags_response:
|
||||
tags.append({'name': image_name, 'tag': tag})
|
||||
|
||||
return tags
|
||||
|
||||
# assumes image_name_and_tag is already error checked to contain "name:tag"
|
||||
def docker_registry_image_delete(self, context, image_name_and_tag):
|
||||
image_name_and_tag = image_name_and_tag.split(":")
|
||||
|
||||
# first get the image digest for the image name and tag provided
|
||||
digest_resp = docker_registry.docker_registry_get("%s/manifests/%s"
|
||||
% (image_name_and_tag[0], image_name_and_tag[1]),
|
||||
self._get_docker_registry_addr())
|
||||
|
||||
if digest_resp.status_code != 200:
|
||||
LOG.error("Bad response from docker registry: %s"
|
||||
% digest_resp.status_code)
|
||||
return
|
||||
|
||||
image_digest = digest_resp.headers['Docker-Content-Digest']
|
||||
|
||||
# now delete the image
|
||||
image_delete_response = docker_registry.docker_registry_delete(
|
||||
"%s/manifests/%s" % (image_name_and_tag[0], image_digest),
|
||||
self._get_docker_registry_addr())
|
||||
|
||||
if image_delete_response.status_code != 202:
|
||||
LOG.error("Bad response from docker registry: %s"
|
||||
% digest_resp.status_code)
|
||||
return
|
||||
|
||||
def docker_registry_garbage_collect(self, context):
|
||||
"""Run garbage collector"""
|
||||
active_controller = utils.HostHelper.get_active_controller(self.dbapi)
|
||||
personalities = [constants.CONTROLLER]
|
||||
config_uuid = self._config_update_hosts(context, personalities,
|
||||
[active_controller.uuid])
|
||||
|
||||
config_dict = {
|
||||
"personalities": personalities,
|
||||
"host_uuids": [active_controller.uuid],
|
||||
"classes": ['platform::dockerdistribution::garbagecollect']
|
||||
}
|
||||
self._config_apply_runtime_manifest(context, config_uuid, config_dict)
|
||||
|
||||
def get_magnum_cluster_count(self, context):
|
||||
return self._openstack.get_magnum_cluster_count()
|
||||
|
||||
|
||||
@@ -843,6 +843,39 @@ class ConductorAPI(sysinv.openstack.common.rpc.proxy.RpcProxy):
|
||||
return self.call(context,
|
||||
self.make_msg('update_remotelogging_config'), timeout=timeout)
|
||||
|
||||
def docker_registry_image_list(self, context):
|
||||
"""Synchronously, request a list of images from Docker Registry API
|
||||
|
||||
:param context: request context.
|
||||
"""
|
||||
return self.call(context,
|
||||
self.make_msg('docker_registry_image_list'))
|
||||
|
||||
def docker_registry_image_tags(self, context, image_name):
|
||||
"""Synchronously, request a list of tags from Docker Registry API for a given image
|
||||
|
||||
:param context: request context.
|
||||
"""
|
||||
return self.call(context,
|
||||
self.make_msg('docker_registry_image_tags', image_name=image_name))
|
||||
|
||||
def docker_registry_image_delete(self, context, image_name_and_tag):
|
||||
"""Synchronously, delete the given image tag from the local docker registry
|
||||
|
||||
:param context: request context.
|
||||
"""
|
||||
return self.call(context,
|
||||
self.make_msg('docker_registry_image_delete',
|
||||
image_name_and_tag=image_name_and_tag))
|
||||
|
||||
def docker_registry_garbage_collect(self, context):
|
||||
"""Asynchronously, run the docker registry garbage collector
|
||||
|
||||
:param context: request context.
|
||||
"""
|
||||
return self.cast(context,
|
||||
self.make_msg('docker_registry_garbage_collect'))
|
||||
|
||||
def get_magnum_cluster_count(self, context):
|
||||
"""Synchronously, have the conductor get magnum cluster count
|
||||
configuration.
|
||||
|
||||
Reference in New Issue
Block a user