
This is the client side change of server patch [1]. If files are copied to container, the file data will be encoded at client side and decoded at server side. If files are copied from container, the file data will be encoded at server side and decoded at client side. [1] https://review.openstack.org/#/c/599202/ Depends-On: https://review.openstack.org/#/c/599202/ Change-Id: Id9fe25e06052d46dcc33a1e05100ef7a2d7db018 Closes-Bug: #1789777
1406 lines
54 KiB
Python
1406 lines
54 KiB
Python
# 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 argparse
|
|
from contextlib import closing
|
|
import io
|
|
import os
|
|
from oslo_log import log as logging
|
|
import tarfile
|
|
import time
|
|
|
|
from osc_lib.command import command
|
|
from osc_lib import utils
|
|
|
|
from zunclient.common import utils as zun_utils
|
|
from zunclient.common.websocketclient import exceptions
|
|
from zunclient.common.websocketclient import websocketclient
|
|
from zunclient import exceptions as exc
|
|
from zunclient.i18n import _
|
|
|
|
|
|
def _container_columns(container):
|
|
return container._info.keys()
|
|
|
|
|
|
def _get_client(obj, parsed_args):
|
|
obj.log.debug("take_action(%s)" % parsed_args)
|
|
return obj.app.client_manager.container
|
|
|
|
|
|
def _action_columns(action):
|
|
return action._info.keys()
|
|
|
|
|
|
class CreateContainer(command.ShowOne):
|
|
"""Create a container"""
|
|
|
|
log = logging.getLogger(__name__ + ".CreateContainer")
|
|
|
|
def get_parser(self, prog_name):
|
|
parser = super(CreateContainer, self).get_parser(prog_name)
|
|
parser.add_argument(
|
|
'--name',
|
|
metavar='<name>',
|
|
help='name of the container')
|
|
parser.add_argument(
|
|
'image',
|
|
metavar='<image>',
|
|
help='name or ID or repo of the image (e.g. cirros:latest)')
|
|
parser.add_argument(
|
|
'--cpu',
|
|
metavar='<cpu>',
|
|
help='The number of virtual cpus.')
|
|
parser.add_argument(
|
|
'--memory',
|
|
metavar='<memory>',
|
|
help='The container memory size in MiB')
|
|
parser.add_argument(
|
|
'--environment',
|
|
metavar='<KEY=VALUE>',
|
|
action='append', default=[],
|
|
help='The environment variables')
|
|
parser.add_argument(
|
|
'--workdir',
|
|
metavar='<workdir>',
|
|
help='The working directory for commands to run in')
|
|
parser.add_argument(
|
|
'--label',
|
|
metavar='<KEY=VALUE>',
|
|
action='append', default=[],
|
|
help='Adds a map of labels to a container. '
|
|
'May be used multiple times.')
|
|
parser.add_argument(
|
|
'--image-pull-policy',
|
|
dest='image_pull_policy',
|
|
metavar='<policy>',
|
|
choices=['never', 'always', 'ifnotpresent'],
|
|
help='The policy which determines if the image should '
|
|
'be pulled prior to starting the container. '
|
|
'It can have following values: '
|
|
'"ifnotpresent": only pull the image if it does not '
|
|
'already exist on the node. '
|
|
'"always": Always pull the image from repository.'
|
|
'"never": never pull the image')
|
|
restart_auto_remove_args = parser.add_mutually_exclusive_group()
|
|
restart_auto_remove_args.add_argument(
|
|
'--restart',
|
|
metavar='<restart>',
|
|
help='Restart policy to apply when a container exits'
|
|
'(no, on-failure[:max-retry], always, unless-stopped)')
|
|
restart_auto_remove_args.add_argument(
|
|
'--auto-remove',
|
|
dest='auto_remove',
|
|
action='store_true',
|
|
default=False,
|
|
help='Automatically remove the container when it exits')
|
|
parser.add_argument(
|
|
'--image-driver',
|
|
metavar='<image_driver>',
|
|
help='The image driver to use to pull container image. '
|
|
'It can have following values: '
|
|
'"docker": pull the image from Docker Hub. '
|
|
'"glance": pull the image from Glance. ')
|
|
parser.add_argument(
|
|
'--interactive',
|
|
dest='interactive',
|
|
action='store_true',
|
|
default=False,
|
|
help='Keep STDIN open even if not attached, allocate a pseudo-TTY')
|
|
secgroup_expose_port_args = parser.add_mutually_exclusive_group()
|
|
secgroup_expose_port_args.add_argument(
|
|
'--security-group',
|
|
metavar='<security_group>',
|
|
action='append', default=[],
|
|
help='The name of security group for the container. '
|
|
'May be used multiple times.')
|
|
secgroup_expose_port_args.add_argument(
|
|
'--expose-port',
|
|
action='append',
|
|
default=[],
|
|
metavar='<port>',
|
|
help='Expose container port(s) to outside (format: '
|
|
'<port>[/<protocol>]).')
|
|
parser.add_argument(
|
|
'command',
|
|
metavar='<command>',
|
|
nargs=argparse.REMAINDER,
|
|
help='Send command to the container')
|
|
parser.add_argument(
|
|
'--hint',
|
|
metavar='<key=value>',
|
|
action='append',
|
|
default=[],
|
|
help='The key-value pair(s) for scheduler to select host. '
|
|
'The format of this parameter is "key=value[,key=value]". '
|
|
'May be used multiple times.')
|
|
parser.add_argument(
|
|
'--net',
|
|
metavar='<network=network, port=port-uuid,'
|
|
'v4-fixed-ip=ip-addr,v6-fixed-ip=ip-addr>',
|
|
action='append',
|
|
default=[],
|
|
help='Create network enpoints for the container. '
|
|
'network: attach container to the specified neutron networks.'
|
|
' port: attach container to the neutron port with this UUID. '
|
|
'v4-fixed-ip: IPv4 fixed address for container. '
|
|
'v6-fixed-ip: IPv6 fixed address for container.')
|
|
parser.add_argument(
|
|
'--mount',
|
|
action='append',
|
|
default=[],
|
|
metavar='<mount>',
|
|
help='A dictionary to configure volumes mounted inside the '
|
|
'container.')
|
|
parser.add_argument(
|
|
'--runtime',
|
|
metavar='<runtime>',
|
|
help='The runtime to use for this container. '
|
|
'It can have value "runc" or any other custom runtime.')
|
|
parser.add_argument(
|
|
'--hostname',
|
|
metavar='<hostname>',
|
|
help='Container host name')
|
|
parser.add_argument(
|
|
'--disk',
|
|
metavar='<disk>',
|
|
type=int,
|
|
default=None,
|
|
help='The disk size in GiB for per container.')
|
|
parser.add_argument(
|
|
'--availability-zone',
|
|
metavar='<availability_zone>',
|
|
default=None,
|
|
help='The availability zone of the container.')
|
|
parser.add_argument(
|
|
'--auto-heal',
|
|
dest='auto_heal',
|
|
action='store_true',
|
|
default=False,
|
|
help='The flag of healing non-existent container in docker')
|
|
parser.add_argument(
|
|
'--privileged',
|
|
dest='privileged',
|
|
action='store_true',
|
|
default=False,
|
|
help='Give extended privileges to this container')
|
|
parser.add_argument(
|
|
'--healthcheck',
|
|
action='append',
|
|
default=[],
|
|
metavar='<cmd=test_cmd,interval=time,retries=n,timeout=time>',
|
|
help='Specify a test cmd to perform to check that the container'
|
|
'is healthy. '
|
|
'cmd: Command to run to check health. '
|
|
'interval: Time between running the check (|s|m|h)'
|
|
' (default 0s). '
|
|
'retries: Consecutive failures needed to report unhealthy.'
|
|
'timeout: Maximum time to allow one check to run (s|m|h)'
|
|
' (default 0s).')
|
|
return parser
|
|
|
|
def take_action(self, parsed_args):
|
|
client = _get_client(self, parsed_args)
|
|
opts = {}
|
|
opts['name'] = parsed_args.name
|
|
opts['image'] = parsed_args.image
|
|
opts['memory'] = parsed_args.memory
|
|
opts['cpu'] = parsed_args.cpu
|
|
opts['environment'] = zun_utils.format_args(parsed_args.environment)
|
|
opts['workdir'] = parsed_args.workdir
|
|
opts['labels'] = zun_utils.format_args(parsed_args.label)
|
|
opts['image_pull_policy'] = parsed_args.image_pull_policy
|
|
opts['image_driver'] = parsed_args.image_driver
|
|
opts['auto_remove'] = parsed_args.auto_remove
|
|
opts['command'] = parsed_args.command
|
|
if parsed_args.security_group:
|
|
opts['security_groups'] = parsed_args.security_group
|
|
if parsed_args.expose_port:
|
|
opts['exposed_ports'] = zun_utils.parse_exposed_ports(
|
|
parsed_args.expose_port)
|
|
if parsed_args.restart:
|
|
opts['restart_policy'] = \
|
|
zun_utils.check_restart_policy(parsed_args.restart)
|
|
if parsed_args.interactive:
|
|
opts['interactive'] = True
|
|
if parsed_args.privileged:
|
|
opts['privileged'] = True
|
|
opts['hints'] = zun_utils.format_args(parsed_args.hint)
|
|
opts['nets'] = zun_utils.parse_nets(parsed_args.net)
|
|
opts['mounts'] = zun_utils.parse_mounts(parsed_args.mount)
|
|
opts['runtime'] = parsed_args.runtime
|
|
opts['hostname'] = parsed_args.hostname
|
|
opts['disk'] = parsed_args.disk
|
|
opts['availability_zone'] = parsed_args.availability_zone
|
|
if parsed_args.auto_heal:
|
|
opts['auto_heal'] = parsed_args.auto_heal
|
|
if parsed_args.healthcheck:
|
|
opts['healthcheck'] = \
|
|
zun_utils.parse_health(parsed_args.healthcheck)
|
|
|
|
opts = zun_utils.remove_null_parms(**opts)
|
|
container = client.containers.create(**opts)
|
|
columns = _container_columns(container)
|
|
return columns, utils.get_item_properties(container, columns)
|
|
|
|
|
|
class ShowContainer(command.ShowOne):
|
|
"""Show a container"""
|
|
|
|
log = logging.getLogger(__name__ + ".ShowContainer")
|
|
|
|
def get_parser(self, prog_name):
|
|
parser = super(ShowContainer, self).get_parser(prog_name)
|
|
parser.add_argument(
|
|
'container',
|
|
metavar='<container>',
|
|
help='ID or name of the container to show.')
|
|
parser.add_argument(
|
|
'--all-projects',
|
|
action="store_true",
|
|
default=False,
|
|
help='Show container(s) in all projects by name.')
|
|
return parser
|
|
|
|
def take_action(self, parsed_args):
|
|
client = _get_client(self, parsed_args)
|
|
opts = {}
|
|
opts['id'] = parsed_args.container
|
|
opts['all_projects'] = parsed_args.all_projects
|
|
opts = zun_utils.remove_null_parms(**opts)
|
|
container = client.containers.get(**opts)
|
|
zun_utils.format_container_addresses(container)
|
|
columns = _container_columns(container)
|
|
|
|
return columns, utils.get_item_properties(container, columns)
|
|
|
|
|
|
class ListContainer(command.Lister):
|
|
"""List available containers"""
|
|
|
|
log = logging.getLogger(__name__ + ".ListContainers")
|
|
|
|
def get_parser(self, prog_name):
|
|
parser = super(ListContainer, self).get_parser(prog_name)
|
|
parser.add_argument(
|
|
'--all-projects',
|
|
action="store_true",
|
|
default=False,
|
|
help='List containers in all projects')
|
|
parser.add_argument(
|
|
'--marker',
|
|
metavar='<marker>',
|
|
help='The last container UUID of the previous page; '
|
|
'displays list of containers after "marker".')
|
|
parser.add_argument(
|
|
'--limit',
|
|
metavar='<limit>',
|
|
type=int,
|
|
help='Maximum number of containers to return')
|
|
parser.add_argument(
|
|
'--sort-key',
|
|
metavar='<sort-key>',
|
|
help='Column to sort results by')
|
|
parser.add_argument(
|
|
'--sort-dir',
|
|
metavar='<sort-dir>',
|
|
choices=['desc', 'asc'],
|
|
help='Direction to sort. "asc" or "desc".')
|
|
parser.add_argument(
|
|
'--name',
|
|
metavar='<name>',
|
|
help='List containers according to their name.')
|
|
parser.add_argument(
|
|
'--image',
|
|
metavar='<image>',
|
|
help='List containers according to their image.')
|
|
parser.add_argument(
|
|
'--project-id',
|
|
metavar='<project-id>',
|
|
help='List containers according to their project_id')
|
|
parser.add_argument(
|
|
'--user-id',
|
|
metavar='<user-id>',
|
|
help='List containers according to their user_id')
|
|
parser.add_argument(
|
|
'--task-state',
|
|
metavar='<task-state>',
|
|
help='List containers according to their task-state')
|
|
parser.add_argument(
|
|
'--status',
|
|
metavar='<status>',
|
|
help='List containers according to their Status')
|
|
parser.add_argument(
|
|
'--memory',
|
|
metavar='<memory>',
|
|
help='List containers according to their memory size in MiB')
|
|
parser.add_argument(
|
|
'--host',
|
|
metavar='<host>',
|
|
help='List containers according to their hostname')
|
|
parser.add_argument(
|
|
'--auto-remove',
|
|
metavar='<auto-remove>',
|
|
help='List containers whether they are auto-removed on exiting')
|
|
return parser
|
|
|
|
def take_action(self, parsed_args):
|
|
client = _get_client(self, parsed_args)
|
|
opts = {}
|
|
opts['all_projects'] = parsed_args.all_projects
|
|
opts['marker'] = parsed_args.marker
|
|
opts['limit'] = parsed_args.limit
|
|
opts['sort_key'] = parsed_args.sort_key
|
|
opts['sort_dir'] = parsed_args.sort_dir
|
|
opts['image'] = parsed_args.image
|
|
opts['name'] = parsed_args.name
|
|
opts['project_id'] = parsed_args.project_id
|
|
opts['user_id'] = parsed_args.user_id
|
|
opts['host'] = parsed_args.host
|
|
opts['task_state'] = parsed_args.task_state
|
|
opts['memory'] = parsed_args.memory
|
|
opts['auto_remove'] = parsed_args.auto_remove
|
|
opts['status'] = parsed_args.status
|
|
opts = zun_utils.remove_null_parms(**opts)
|
|
containers = client.containers.list(**opts)
|
|
for c in containers:
|
|
zun_utils.format_container_addresses(c)
|
|
columns = ('uuid', 'name', 'image', 'status', 'task_state',
|
|
'addresses', 'ports')
|
|
return (columns, (utils.get_item_properties(container, columns)
|
|
for container in containers))
|
|
|
|
|
|
class DeleteContainer(command.Command):
|
|
"""Delete specified container(s)"""
|
|
|
|
log = logging.getLogger(__name__ + ".Deletecontainer")
|
|
|
|
def get_parser(self, prog_name):
|
|
parser = super(DeleteContainer, self).get_parser(prog_name)
|
|
parser.add_argument(
|
|
'container',
|
|
metavar='<container>',
|
|
nargs='+',
|
|
help='ID or name of the container(s) to delete.')
|
|
parser.add_argument(
|
|
'--force',
|
|
action='store_true',
|
|
help='Force delete the container.')
|
|
parser.add_argument(
|
|
'--stop',
|
|
action='store_true',
|
|
help='Stop the running container first before delete.')
|
|
parser.add_argument(
|
|
'--all-projects',
|
|
action="store_true",
|
|
default=False,
|
|
help='Delete container(s) in all projects by name.')
|
|
return parser
|
|
|
|
def take_action(self, parsed_args):
|
|
client = _get_client(self, parsed_args)
|
|
containers = parsed_args.container
|
|
for container in containers:
|
|
opts = {}
|
|
opts['id'] = container
|
|
opts['force'] = parsed_args.force
|
|
opts['stop'] = parsed_args.stop
|
|
opts['all_projects'] = parsed_args.all_projects
|
|
opts = zun_utils.remove_null_parms(**opts)
|
|
try:
|
|
client.containers.delete(**opts)
|
|
print(_('Request to delete container %s has been accepted.')
|
|
% container)
|
|
except Exception as e:
|
|
print("Delete for container %(container)s failed: %(e)s" %
|
|
{'container': container, 'e': e})
|
|
|
|
|
|
class RestartContainer(command.Command):
|
|
"""Restart specified container"""
|
|
log = logging.getLogger(__name__ + ".RestartContainer")
|
|
|
|
def get_parser(self, prog_name):
|
|
parser = super(RestartContainer, self).get_parser(prog_name)
|
|
parser.add_argument(
|
|
'container',
|
|
metavar='<container>',
|
|
nargs='+',
|
|
help='ID or name of the (container)s to restart.')
|
|
parser.add_argument(
|
|
'--timeout',
|
|
metavar='<timeout>',
|
|
default=10,
|
|
help='Seconds to wait for stop before restarting (container)s')
|
|
return parser
|
|
|
|
def take_action(self, parsed_args):
|
|
client = _get_client(self, parsed_args)
|
|
containers = parsed_args.container
|
|
for container in containers:
|
|
try:
|
|
client.containers.restart(container, parsed_args.timeout)
|
|
print(_('Request to restart container %s has been accepted')
|
|
% container)
|
|
except Exception as e:
|
|
print("Restart for container %(container)s failed: %(e)s" %
|
|
{'container': container, 'e': e})
|
|
|
|
|
|
class StartContainer(command.Command):
|
|
"""Start specified container"""
|
|
log = logging.getLogger(__name__ + ".StartContainer")
|
|
|
|
def get_parser(self, prog_name):
|
|
parser = super(StartContainer, self).get_parser(prog_name)
|
|
parser.add_argument(
|
|
'container',
|
|
metavar='<container>',
|
|
nargs='+',
|
|
help='ID or name of the (container)s to start.')
|
|
return parser
|
|
|
|
def take_action(self, parsed_args):
|
|
client = _get_client(self, parsed_args)
|
|
containers = parsed_args.container
|
|
for container in containers:
|
|
try:
|
|
client.containers.start(container)
|
|
print(_('Request to start container %s has been accepted')
|
|
% container)
|
|
except Exception as e:
|
|
print("Start for container %(container)s failed: %(e)s" %
|
|
{'container': container, 'e': e})
|
|
|
|
|
|
class PauseContainer(command.Command):
|
|
"""Pause specified container"""
|
|
log = logging.getLogger(__name__ + ".PauseContainer")
|
|
|
|
def get_parser(self, prog_name):
|
|
parser = super(PauseContainer, self).get_parser(prog_name)
|
|
parser.add_argument(
|
|
'container',
|
|
metavar='<container>',
|
|
nargs='+',
|
|
help='ID or name of the (container)s to pause.')
|
|
return parser
|
|
|
|
def take_action(self, parsed_args):
|
|
client = _get_client(self, parsed_args)
|
|
containers = parsed_args.container
|
|
for container in containers:
|
|
try:
|
|
client.containers.pause(container)
|
|
print(_('Request to pause container %s has been accepted')
|
|
% container)
|
|
except Exception as e:
|
|
print("Pause for container %(container)s failed: %(e)s" %
|
|
{'container': container, 'e': e})
|
|
|
|
|
|
class UnpauseContainer(command.Command):
|
|
"""unpause specified container"""
|
|
log = logging.getLogger(__name__ + ".UnpauseContainer")
|
|
|
|
def get_parser(self, prog_name):
|
|
parser = super(UnpauseContainer, self).get_parser(prog_name)
|
|
parser.add_argument(
|
|
'container',
|
|
metavar='<container>',
|
|
nargs='+',
|
|
help='ID or name of the (container)s to unpause.')
|
|
return parser
|
|
|
|
def take_action(self, parsed_args):
|
|
client = _get_client(self, parsed_args)
|
|
containers = parsed_args.container
|
|
for container in containers:
|
|
try:
|
|
client.containers.unpause(container)
|
|
print(_('Request to unpause container %s has been accepted')
|
|
% container)
|
|
except Exception as e:
|
|
print("unpause for container %(container)s failed: %(e)s" %
|
|
{'container': container, 'e': e})
|
|
|
|
|
|
class ExecContainer(command.Command):
|
|
"""Execute command in a running container"""
|
|
log = logging.getLogger(__name__ + ".ExecContainer")
|
|
|
|
def get_parser(self, prog_name):
|
|
parser = super(ExecContainer, self).get_parser(prog_name)
|
|
parser.add_argument(
|
|
'container',
|
|
metavar='<container>',
|
|
help='ID or name of the container to execute command in.')
|
|
parser.add_argument(
|
|
'command',
|
|
metavar='<command>',
|
|
nargs=argparse.REMAINDER,
|
|
help='The command to execute.')
|
|
parser.add_argument(
|
|
'--interactive',
|
|
dest='interactive',
|
|
action='store_true',
|
|
default=False,
|
|
help='Keep STDIN open and allocate a pseudo-TTY for interactive')
|
|
return parser
|
|
|
|
def take_action(self, parsed_args):
|
|
client = _get_client(self, parsed_args)
|
|
container = parsed_args.container
|
|
opts = {}
|
|
opts['command'] = zun_utils.parse_command(parsed_args.command)
|
|
if parsed_args.interactive:
|
|
opts['interactive'] = True
|
|
opts['run'] = False
|
|
response = client.containers.execute(container, **opts)
|
|
if parsed_args.interactive:
|
|
exec_id = response['exec_id']
|
|
url = response['proxy_url']
|
|
websocketclient.do_exec(client, url, container, exec_id, "~", 0.5)
|
|
else:
|
|
output = response['output']
|
|
exit_code = response['exit_code']
|
|
print(output)
|
|
return exit_code
|
|
|
|
|
|
class LogsContainer(command.Command):
|
|
"""Get logs of a container"""
|
|
log = logging.getLogger(__name__ + ".LogsContainer")
|
|
|
|
def get_parser(self, prog_name):
|
|
parser = super(LogsContainer, self).get_parser(prog_name)
|
|
parser.add_argument(
|
|
'container',
|
|
metavar='<container>',
|
|
help='ID or name of the container to get logs for.')
|
|
parser.add_argument(
|
|
'--stdout',
|
|
action='store_true',
|
|
help='Only stdout logs of container.')
|
|
parser.add_argument(
|
|
'--stderr',
|
|
action='store_true',
|
|
help='Only stderr logs of container.')
|
|
parser.add_argument(
|
|
'--since',
|
|
metavar='<since>',
|
|
default=None,
|
|
help='Show logs since a given datetime or integer '
|
|
'epoch (in seconds).')
|
|
parser.add_argument(
|
|
'--timestamps',
|
|
dest='timestamps',
|
|
action='store_true',
|
|
default=False,
|
|
help='Show timestamps.')
|
|
parser.add_argument(
|
|
'--tail',
|
|
metavar='<tail>',
|
|
default='all',
|
|
help='Number of lines to show from the end of the logs.')
|
|
return parser
|
|
|
|
def take_action(self, parsed_args):
|
|
client = _get_client(self, parsed_args)
|
|
opts = {}
|
|
opts['id'] = parsed_args.container
|
|
opts['stdout'] = parsed_args.stdout
|
|
opts['stderr'] = parsed_args.stderr
|
|
opts['since'] = parsed_args.since
|
|
opts['timestamps'] = parsed_args.timestamps
|
|
opts['tail'] = parsed_args.tail
|
|
opts = zun_utils.remove_null_parms(**opts)
|
|
logs = client.containers.logs(**opts)
|
|
print(logs)
|
|
|
|
|
|
class KillContainer(command.Command):
|
|
"""Kill one or more running container(s)"""
|
|
log = logging.getLogger(__name__ + ".KillContainers")
|
|
|
|
def get_parser(self, prog_name):
|
|
parser = super(KillContainer, self).get_parser(prog_name)
|
|
parser.add_argument(
|
|
'containers',
|
|
metavar='<container>',
|
|
nargs='+',
|
|
help='ID or name of the (container)s to kill.')
|
|
parser.add_argument(
|
|
'--signal',
|
|
metavar='<signal>',
|
|
default=None,
|
|
help='The signal to kill')
|
|
return parser
|
|
|
|
def take_action(self, parsed_args):
|
|
client = _get_client(self, parsed_args)
|
|
for container in parsed_args.containers:
|
|
opts = {}
|
|
opts['id'] = container
|
|
opts['signal'] = parsed_args.signal
|
|
opts = zun_utils.remove_null_parms(**opts)
|
|
try:
|
|
client.containers.kill(**opts)
|
|
print(_('Request to send kill signal to container %s has '
|
|
'been accepted') % container)
|
|
except Exception as e:
|
|
print("kill signal for container %(container)s failed: %(e)s" %
|
|
{'container': container, 'e': e})
|
|
|
|
|
|
class StopContainer(command.Command):
|
|
"""Stop specified containers"""
|
|
|
|
log = logging.getLogger(__name__ + ".StopContainer")
|
|
|
|
def get_parser(self, prog_name):
|
|
parser = super(StopContainer, self).get_parser(prog_name)
|
|
parser.add_argument(
|
|
'container',
|
|
metavar='<container>',
|
|
nargs='+',
|
|
help='ID or name of the (container)s to stop.')
|
|
parser.add_argument(
|
|
'--timeout',
|
|
metavar='<timeout>',
|
|
default=10,
|
|
help='Seconds to wait for stop before killing (container)s')
|
|
return parser
|
|
|
|
def take_action(self, parsed_args):
|
|
client = _get_client(self, parsed_args)
|
|
containers = parsed_args.container
|
|
for container in containers:
|
|
try:
|
|
client.containers.stop(container, parsed_args.timeout)
|
|
print(_('Request to stop container %s has been accepted.')
|
|
% container)
|
|
except Exception as e:
|
|
print("Stop for container %(container)s failed: %(e)s" %
|
|
{'container': container, 'e': e})
|
|
|
|
|
|
class RunContainer(command.ShowOne):
|
|
"""Create and run a new container"""
|
|
|
|
log = logging.getLogger(__name__ + ".RunContainer")
|
|
|
|
def get_parser(self, prog_name):
|
|
parser = super(RunContainer, self).get_parser(prog_name)
|
|
parser.add_argument(
|
|
'--name',
|
|
metavar='<name>',
|
|
help='name of the container')
|
|
parser.add_argument(
|
|
'image',
|
|
metavar='<image>',
|
|
help='name or ID of the image')
|
|
parser.add_argument(
|
|
'--cpu',
|
|
metavar='<cpu>',
|
|
help='The number of virtual cpus.')
|
|
parser.add_argument(
|
|
'--memory',
|
|
metavar='<memory>',
|
|
help='The container memory size in MiB')
|
|
parser.add_argument(
|
|
'--environment',
|
|
metavar='<KEY=VALUE>',
|
|
action='append', default=[],
|
|
help='The environment variables')
|
|
parser.add_argument(
|
|
'--workdir',
|
|
metavar='<workdir>',
|
|
help='The working directory for commands to run in')
|
|
parser.add_argument(
|
|
'--label',
|
|
metavar='<KEY=VALUE>',
|
|
action='append', default=[],
|
|
help='Adds a map of labels to a container. '
|
|
'May be used multiple times.')
|
|
parser.add_argument(
|
|
'--image-pull-policy',
|
|
dest='image_pull_policy',
|
|
metavar='<policy>',
|
|
choices=['never', 'always', 'ifnotpresent'],
|
|
help='The policy which determines if the image should '
|
|
'be pulled prior to starting the container. '
|
|
'It can have following values: '
|
|
'"ifnotpresent": only pull the image if it does not '
|
|
'already exist on the node. '
|
|
'"always": Always pull the image from repository.'
|
|
'"never": never pull the image')
|
|
restart_auto_remove_args = parser.add_mutually_exclusive_group()
|
|
restart_auto_remove_args.add_argument(
|
|
'--restart',
|
|
metavar='<restart>',
|
|
help='Restart policy to apply when a container exits'
|
|
'(no, on-failure[:max-retry], always, unless-stopped)')
|
|
restart_auto_remove_args.add_argument(
|
|
'--auto-remove',
|
|
dest='auto_remove',
|
|
action='store_true',
|
|
default=False,
|
|
help='Automatically remove the container when it exits')
|
|
parser.add_argument(
|
|
'--image-driver',
|
|
metavar='<image_driver>',
|
|
help='The image driver to use to pull container image. '
|
|
'It can have following values: '
|
|
'"docker": pull the image from Docker Hub. '
|
|
'"glance": pull the image from Glance. ')
|
|
parser.add_argument(
|
|
'--interactive',
|
|
dest='interactive',
|
|
action='store_true',
|
|
default=False,
|
|
help='Keep STDIN open even if not attached, allocate a pseudo-TTY')
|
|
secgroup_expose_port_args = parser.add_mutually_exclusive_group()
|
|
secgroup_expose_port_args.add_argument(
|
|
'--security-group',
|
|
metavar='<security_group>',
|
|
action='append', default=[],
|
|
help='The name of security group for the container. '
|
|
'May be used multiple times.')
|
|
secgroup_expose_port_args.add_argument(
|
|
'--expose-port',
|
|
action='append',
|
|
default=[],
|
|
metavar='<port>',
|
|
help='Expose container port(s) to outside (format: '
|
|
'<port>[/<protocol>]).')
|
|
parser.add_argument(
|
|
'command',
|
|
metavar='<command>',
|
|
nargs=argparse.REMAINDER,
|
|
help='Send command to the container')
|
|
parser.add_argument(
|
|
'--hint',
|
|
metavar='<key=value>',
|
|
action='append',
|
|
default=[],
|
|
help='The key-value pair(s) for scheduler to select host. '
|
|
'The format of this parameter is "key=value[,key=value]". '
|
|
'May be used multiple times.')
|
|
parser.add_argument(
|
|
'--net',
|
|
metavar='<network=network, port=port-uuid,'
|
|
'v4-fixed-ip=ip-addr,v6-fixed-ip=ip-addr>',
|
|
action='append',
|
|
default=[],
|
|
help='Create network enpoints for the container. '
|
|
'network: attach container to the specified neutron networks.'
|
|
' port: attach container to the neutron port with this UUID. '
|
|
'v4-fixed-ip: IPv4 fixed address for container. '
|
|
'v6-fixed-ip: IPv6 fixed address for container.')
|
|
parser.add_argument(
|
|
'--mount',
|
|
action='append',
|
|
default=[],
|
|
metavar='<mount>',
|
|
help='A dictionary to configure volumes mounted inside the '
|
|
'container.')
|
|
parser.add_argument(
|
|
'--runtime',
|
|
metavar='<runtime>',
|
|
help='The runtime to use for this container. '
|
|
'It can have value "runc" or any other custom runtime.')
|
|
parser.add_argument(
|
|
'--hostname',
|
|
metavar='<hostname>',
|
|
help='Container host name')
|
|
parser.add_argument(
|
|
'--disk',
|
|
metavar='<disk>',
|
|
type=int,
|
|
default=None,
|
|
help='The disk size in GiB for per container.')
|
|
parser.add_argument(
|
|
'--availability-zone',
|
|
metavar='<availability_zone>',
|
|
default=None,
|
|
help='The availability zone of the container.')
|
|
parser.add_argument(
|
|
'--auto-heal',
|
|
dest='auto_heal',
|
|
action='store_true',
|
|
default=False,
|
|
help='The flag of healing non-existent container in docker')
|
|
parser.add_argument(
|
|
'--privileged',
|
|
dest='privileged',
|
|
action='store_true',
|
|
default=False,
|
|
help='Give extended privileges to this container')
|
|
parser.add_argument(
|
|
'--healthcheck',
|
|
action='append',
|
|
default=[],
|
|
metavar='<cmd=test_cmd,interval=time,retries=n,timeout=time>',
|
|
help='Specify a test cmd to perform to check that the container'
|
|
'is healthy. '
|
|
'cmd: Command to run to check health. '
|
|
'interval: Time between running the check (s|m|h)'
|
|
' (default 0s). '
|
|
'retries: Consecutive failures needed to report unhealthy.'
|
|
'timeout: Maximum time to allow one check to run (s|m|h)'
|
|
' (default 0s).')
|
|
return parser
|
|
|
|
def take_action(self, parsed_args):
|
|
client = _get_client(self, parsed_args)
|
|
opts = {}
|
|
opts['name'] = parsed_args.name
|
|
opts['image'] = parsed_args.image
|
|
opts['memory'] = parsed_args.memory
|
|
opts['cpu'] = parsed_args.cpu
|
|
opts['environment'] = zun_utils.format_args(parsed_args.environment)
|
|
opts['workdir'] = parsed_args.workdir
|
|
opts['labels'] = zun_utils.format_args(parsed_args.label)
|
|
opts['image_pull_policy'] = parsed_args.image_pull_policy
|
|
opts['image_driver'] = parsed_args.image_driver
|
|
opts['auto_remove'] = parsed_args.auto_remove
|
|
opts['command'] = parsed_args.command
|
|
if parsed_args.security_group:
|
|
opts['security_groups'] = parsed_args.security_group
|
|
if parsed_args.expose_port:
|
|
opts['exposed_ports'] = zun_utils.parse_exposed_ports(
|
|
parsed_args.expose_port)
|
|
if parsed_args.restart:
|
|
opts['restart_policy'] = \
|
|
zun_utils.check_restart_policy(parsed_args.restart)
|
|
if parsed_args.interactive:
|
|
opts['interactive'] = True
|
|
if parsed_args.privileged:
|
|
opts['privileged'] = True
|
|
opts['hints'] = zun_utils.format_args(parsed_args.hint)
|
|
opts['nets'] = zun_utils.parse_nets(parsed_args.net)
|
|
opts['mounts'] = zun_utils.parse_mounts(parsed_args.mount)
|
|
opts['runtime'] = parsed_args.runtime
|
|
opts['hostname'] = parsed_args.hostname
|
|
opts['disk'] = parsed_args.disk
|
|
opts['availability_zone'] = parsed_args.availability_zone
|
|
if parsed_args.auto_heal:
|
|
opts['auto_heal'] = parsed_args.auto_heal
|
|
if parsed_args.healthcheck:
|
|
opts['healthcheck'] = \
|
|
zun_utils.parse_health(parsed_args.healthcheck)
|
|
|
|
opts = zun_utils.remove_null_parms(**opts)
|
|
container = client.containers.run(**opts)
|
|
columns = _container_columns(container)
|
|
container_uuid = getattr(container, 'uuid', None)
|
|
if parsed_args.interactive:
|
|
ready_for_attach = False
|
|
while True:
|
|
container = client.containers.get(container_uuid)
|
|
if zun_utils.check_container_status(container, 'Running'):
|
|
ready_for_attach = True
|
|
break
|
|
if zun_utils.check_container_status(container, 'Error'):
|
|
break
|
|
print("Waiting for container start")
|
|
time.sleep(1)
|
|
if ready_for_attach is True:
|
|
response = client.containers.attach(container_uuid)
|
|
websocketclient.do_attach(client, response, container_uuid,
|
|
"~", 0.5)
|
|
else:
|
|
raise exceptions.InvalidWebSocketLink(container_uuid)
|
|
|
|
return columns, utils.get_item_properties(container, columns)
|
|
|
|
|
|
class TopContainer(command.Command):
|
|
"""Display the running processes inside the container"""
|
|
log = logging.getLogger(__name__ + ".TopContainer")
|
|
|
|
def get_parser(self, prog_name):
|
|
parser = super(TopContainer, self).get_parser(prog_name)
|
|
parser.add_argument(
|
|
'container',
|
|
metavar='<container>',
|
|
help='ID or name of the container to display processes.')
|
|
parser.add_argument(
|
|
'--pid',
|
|
metavar='<pid>',
|
|
action='append', default=[],
|
|
help='The args of the ps id.')
|
|
return parser
|
|
|
|
def take_action(self, parsed_args):
|
|
client = _get_client(self, parsed_args)
|
|
if parsed_args.pid:
|
|
# List container single ps id top result
|
|
output = client.containers.top(parsed_args.container,
|
|
' '.join(parsed_args.pid))
|
|
else:
|
|
# List container all processes top result
|
|
output = client.containers.top(parsed_args.container)
|
|
|
|
for titles in output['Titles']:
|
|
print("%-20s") % titles,
|
|
if output['Processes']:
|
|
for process in output['Processes']:
|
|
print("")
|
|
for info in process:
|
|
print("%-20s") % info,
|
|
else:
|
|
print("")
|
|
|
|
|
|
class UpdateContainer(command.ShowOne):
|
|
"""Update one or more attributes of the container"""
|
|
log = logging.getLogger(__name__ + ".UpdateContainer")
|
|
|
|
def get_parser(self, prog_name):
|
|
parser = super(UpdateContainer, self).get_parser(prog_name)
|
|
parser.add_argument(
|
|
'container',
|
|
metavar='<container>',
|
|
help="ID or name of the container to update.")
|
|
parser.add_argument(
|
|
'--cpu',
|
|
metavar='<cpu>',
|
|
help='The number of virtual cpus.')
|
|
parser.add_argument(
|
|
'--memory',
|
|
metavar='<memory>',
|
|
help='The container memory size in MiB')
|
|
parser.add_argument(
|
|
'--name',
|
|
metavar='<name>',
|
|
help='The new name of container to update')
|
|
auto_heal_value = parser.add_mutually_exclusive_group()
|
|
auto_heal_value.add_argument(
|
|
'--auto-heal',
|
|
required=False,
|
|
action='store_true',
|
|
help='Automatic recovery the status of contaier')
|
|
auto_heal_value.add_argument(
|
|
'--no-auto-heal',
|
|
required=False,
|
|
action='store_true',
|
|
help='Needless recovery the status of contaier')
|
|
return parser
|
|
|
|
def take_action(self, parsed_args):
|
|
client = _get_client(self, parsed_args)
|
|
container = parsed_args.container
|
|
opts = {}
|
|
opts['memory'] = parsed_args.memory
|
|
opts['cpu'] = parsed_args.cpu
|
|
opts['name'] = parsed_args.name
|
|
if 'auto_heal' in parsed_args and parsed_args.auto_heal:
|
|
opts['auto_heal'] = True
|
|
if 'no_auto_heal' in parsed_args and parsed_args.no_auto_heal:
|
|
opts['auto_heal'] = False
|
|
opts = zun_utils.remove_null_parms(**opts)
|
|
if not opts:
|
|
raise exc.CommandError("You must update at least one property")
|
|
container = client.containers.update(container, **opts)
|
|
columns = _container_columns(container)
|
|
return columns, utils.get_item_properties(container, columns)
|
|
|
|
|
|
class AttachContainer(command.Command):
|
|
"""Attach to a running container"""
|
|
|
|
log = logging.getLogger(__name__ + ".AttachContainer")
|
|
|
|
def get_parser(self, prog_name):
|
|
parser = super(AttachContainer, self).get_parser(prog_name)
|
|
parser.add_argument(
|
|
'container',
|
|
metavar='<container>',
|
|
help='ID or name of the container to be attached to.')
|
|
return parser
|
|
|
|
def take_action(self, parsed_args):
|
|
client = _get_client(self, parsed_args)
|
|
response = client.containers.attach(parsed_args.container)
|
|
websocketclient.do_attach(client, response, parsed_args.container,
|
|
"~", 0.5)
|
|
|
|
|
|
class CopyContainer(command.Command):
|
|
"""Copy files/tars between a container and the local filesystem."""
|
|
log = logging.getLogger(__name__ + ".CopyContainer")
|
|
|
|
def get_parser(self, prog_name):
|
|
parser = super(CopyContainer, self).get_parser(prog_name)
|
|
parser.add_argument(
|
|
'source',
|
|
metavar='<source>',
|
|
help='The source should be copied to the container or localhost. '
|
|
'The format of this parameter is [container:]src_path.')
|
|
parser.add_argument(
|
|
'destination',
|
|
metavar='<destination>',
|
|
help='The directory destination where save the source. '
|
|
'The format of this parameter is [container:]dest_path.')
|
|
return parser
|
|
|
|
def take_action(self, parsed_args):
|
|
client = _get_client(self, parsed_args)
|
|
if ':' in parsed_args.source:
|
|
source_parts = parsed_args.source.split(':', 1)
|
|
container_id = source_parts[0]
|
|
container_path = source_parts[1]
|
|
opts = {}
|
|
opts['id'] = container_id
|
|
opts['path'] = container_path
|
|
|
|
res = client.containers.get_archive(**opts)
|
|
dest_path = parsed_args.destination
|
|
tardata = io.BytesIO(res['data'])
|
|
with closing(tarfile.open(fileobj=tardata)) as tar:
|
|
tar.extractall(dest_path)
|
|
|
|
elif ':' in parsed_args.destination:
|
|
dest_parts = parsed_args.destination.split(':', 1)
|
|
container_id = dest_parts[0]
|
|
container_path = dest_parts[1]
|
|
filename = os.path.split(parsed_args.source)[1]
|
|
opts = {}
|
|
opts['id'] = container_id
|
|
opts['path'] = container_path
|
|
tardata = io.BytesIO()
|
|
with closing(tarfile.open(fileobj=tardata, mode='w')) as tar:
|
|
tar.add(parsed_args.source, arcname=filename)
|
|
opts['data'] = tardata.getvalue()
|
|
client.containers.put_archive(**opts)
|
|
else:
|
|
print("Please check the parameters for zun copy!")
|
|
print("Usage:")
|
|
print("openstack appcontainer cp container:src_path dest_path|-")
|
|
print("openstack appcontainer cp src_path|- container:dest_path")
|
|
|
|
|
|
class StatsContainer(command.ShowOne):
|
|
"""Display stats of the container."""
|
|
log = logging.getLogger(__name__ + ".StatsContainer")
|
|
|
|
def get_parser(self, prog_name):
|
|
parser = super(StatsContainer, self).get_parser(prog_name)
|
|
parser.add_argument(
|
|
'container',
|
|
metavar='<container>',
|
|
help='ID or name of the (container)s to display stats.')
|
|
return parser
|
|
|
|
def take_action(self, parsed_args):
|
|
client = _get_client(self, parsed_args)
|
|
container = parsed_args.container
|
|
stats_info = client.containers.stats(container)
|
|
return stats_info.keys(), stats_info.values()
|
|
|
|
|
|
class CommitContainer(command.Command):
|
|
"""Create a new image from a container's changes"""
|
|
log = logging.getLogger(__name__ + ".CommitContainer")
|
|
|
|
def get_parser(self, prog_name):
|
|
parser = super(CommitContainer, self).get_parser(prog_name)
|
|
parser.add_argument(
|
|
'container',
|
|
metavar='<container>',
|
|
help='ID or name of the (container)s to commit.')
|
|
parser.add_argument(
|
|
'repository',
|
|
metavar='<repository>[:<tag>]',
|
|
help='Repository and tag of the new image.')
|
|
return parser
|
|
|
|
def take_action(self, parsed_args):
|
|
client = _get_client(self, parsed_args)
|
|
container = parsed_args.container
|
|
opts = zun_utils.check_commit_container_args(parsed_args)
|
|
opts = zun_utils.remove_null_parms(**opts)
|
|
try:
|
|
image = client.containers.commit(container, **opts)
|
|
print("Request to commit container %s has been accepted. "
|
|
"The image is %s." % (container, image['uuid']))
|
|
except Exception as e:
|
|
print("commit container %(container)s failed: %(e)s" %
|
|
{'container': container, 'e': e})
|
|
|
|
|
|
class AddSecurityGroup(command.Command):
|
|
"""Add security group for specified container."""
|
|
log = logging.getLogger(__name__ + ".AddSecurityGroup")
|
|
|
|
def get_parser(self, prog_name):
|
|
parser = super(AddSecurityGroup, self).get_parser(prog_name)
|
|
parser.add_argument(
|
|
'container',
|
|
metavar='<container>',
|
|
help='ID or name of the container to add security group.')
|
|
parser.add_argument(
|
|
'security_group',
|
|
metavar='<security_group>',
|
|
help='Security group ID or name for specified container. ')
|
|
return parser
|
|
|
|
def take_action(self, parsed_args):
|
|
client = _get_client(self, parsed_args)
|
|
opts = {}
|
|
opts['id'] = parsed_args.container
|
|
opts['security_group'] = parsed_args.security_group
|
|
opts = zun_utils.remove_null_parms(**opts)
|
|
try:
|
|
# TODO(hongbin): add_security_group is removed starting from
|
|
# API version 1.15. Use Neutron APIs to add security groups
|
|
# to container's ports instead.
|
|
client.containers.add_security_group(**opts)
|
|
print("Request to add security group for container %s "
|
|
"has been accepted." % parsed_args.container)
|
|
except Exception as e:
|
|
print("Add security group for container %(container)s failed: "
|
|
"%(e)s" % {'container': parsed_args.container, 'e': e})
|
|
|
|
|
|
class RemoveSecurityGroup(command.Command):
|
|
"""Remove security group for specified container."""
|
|
log = logging.getLogger(__name__ + ".RemoveSecurityGroup")
|
|
|
|
def get_parser(self, prog_name):
|
|
parser = super(RemoveSecurityGroup, self).get_parser(prog_name)
|
|
parser.add_argument(
|
|
'container',
|
|
metavar='<container>',
|
|
help='ID or name of the container to remove security group.')
|
|
parser.add_argument(
|
|
'security_group',
|
|
metavar='<security_group>',
|
|
help='The security group to remove from specified container. ')
|
|
return parser
|
|
|
|
def take_action(self, parsed_args):
|
|
client = _get_client(self, parsed_args)
|
|
opts = {}
|
|
opts['id'] = parsed_args.container
|
|
opts['security_group'] = parsed_args.security_group
|
|
opts = zun_utils.remove_null_parms(**opts)
|
|
try:
|
|
# TODO(hongbin): remove_security_group is removed starting from
|
|
# API version 1.15. Use Neutron APIs to remove security groups
|
|
# from container's ports instead.
|
|
client.containers.remove_security_group(**opts)
|
|
print("Request to remove security group from container %s "
|
|
"has been accepted." % parsed_args.container)
|
|
except Exception as e:
|
|
print("Remove security group from container %(container)s failed: "
|
|
"%(e)s" % {'container': parsed_args.container, 'e': e})
|
|
|
|
|
|
class NetworkDetach(command.Command):
|
|
"""Detach neutron network from specified container."""
|
|
log = logging.getLogger(__name__ + ".NetworkDetach")
|
|
|
|
def get_parser(self, prog_name):
|
|
parser = super(NetworkDetach, self).get_parser(prog_name)
|
|
parser.add_argument(
|
|
'container',
|
|
metavar='<container>',
|
|
help='ID or name of the container to detach network.')
|
|
network_port_args = parser.add_mutually_exclusive_group()
|
|
network_port_args.add_argument(
|
|
'--network',
|
|
metavar='<network>',
|
|
help='The network for specified container to detach.')
|
|
network_port_args.add_argument(
|
|
'--port',
|
|
metavar='<port>',
|
|
help='The port for specified container to detach.')
|
|
return parser
|
|
|
|
def take_action(self, parsed_args):
|
|
client = _get_client(self, parsed_args)
|
|
opts = {}
|
|
opts['container'] = parsed_args.container
|
|
opts['network'] = parsed_args.network
|
|
opts['port'] = parsed_args.port
|
|
opts = zun_utils.remove_null_parms(**opts)
|
|
try:
|
|
client.containers.network_detach(**opts)
|
|
print("Request to detach network for container %s "
|
|
"has been accepted." % parsed_args.container)
|
|
except Exception as e:
|
|
print("Detach network for container %(container)s failed: "
|
|
"%(e)s" % {'container': parsed_args.container, 'e': e})
|
|
|
|
|
|
class NetworkAttach(command.Command):
|
|
"""Attach neutron network to specified container."""
|
|
log = logging.getLogger(__name__ + ".NetworkAttach")
|
|
|
|
def get_parser(self, prog_name):
|
|
parser = super(NetworkAttach, self).get_parser(prog_name)
|
|
parser.add_argument(
|
|
'container',
|
|
metavar='<container>',
|
|
help='ID or name of the container to attach network.')
|
|
parser.add_argument(
|
|
'--network',
|
|
metavar='<network>',
|
|
help='The network for specified container to attach.')
|
|
parser.add_argument(
|
|
'--port',
|
|
metavar='<port>',
|
|
help='The port for specified container to attach.')
|
|
parser.add_argument(
|
|
'--fixed-ip',
|
|
metavar='<fixed_ip>',
|
|
help='The fixed-ip that container will attach to.')
|
|
return parser
|
|
|
|
def take_action(self, parsed_args):
|
|
client = _get_client(self, parsed_args)
|
|
opts = {}
|
|
opts['container'] = parsed_args.container
|
|
opts['network'] = parsed_args.network
|
|
opts['port'] = parsed_args.port
|
|
opts['fixed_ip'] = parsed_args.fixed_ip
|
|
opts = zun_utils.remove_null_parms(**opts)
|
|
try:
|
|
client.containers.network_attach(**opts)
|
|
print("Request to attach network to container %s "
|
|
"has been accepted." % parsed_args.container)
|
|
except Exception as e:
|
|
print("Attach network to container %(container)s failed: "
|
|
"%(e)s" % {'container': parsed_args.container, 'e': e})
|
|
|
|
|
|
class RebuildContainer(command.Command):
|
|
"""Rebuild one or more running container(s)"""
|
|
log = logging.getLogger(__name__ + ".RebuildContainer")
|
|
|
|
def get_parser(self, prog_name):
|
|
parser = super(RebuildContainer, self).get_parser(prog_name)
|
|
parser.add_argument(
|
|
'containers',
|
|
metavar='<container>',
|
|
nargs='+',
|
|
help='ID or name of the (container)s to rebuild.')
|
|
parser.add_argument(
|
|
'--image',
|
|
metavar='<image>',
|
|
help='The image for specified container to update.')
|
|
parser.add_argument(
|
|
'--image-driver',
|
|
metavar='<image_driver>',
|
|
help='The image driver to use to update container image. '
|
|
'It can have following values: '
|
|
'"docker": update the image from Docker Hub. '
|
|
'"glance": update the image from Glance. '
|
|
'The default value is source container\'s image driver ')
|
|
return parser
|
|
|
|
def take_action(self, parsed_args):
|
|
client = _get_client(self, parsed_args)
|
|
for container in parsed_args.containers:
|
|
opts = {}
|
|
opts['id'] = container
|
|
if parsed_args.image:
|
|
opts['image'] = parsed_args.image
|
|
if parsed_args.image_driver:
|
|
opts['image_driver'] = parsed_args.image_driver
|
|
try:
|
|
client.containers.rebuild(**opts)
|
|
print(_('Request to rebuild container %s has '
|
|
'been accepted') % container)
|
|
except Exception as e:
|
|
print("rebuild container %(container)s failed: %(e)s" %
|
|
{'container': container, 'e': e})
|
|
|
|
|
|
class NetworkList(command.Lister):
|
|
"""List networks on a container"""
|
|
log = logging.getLogger(__name__ + ".ListNetworks")
|
|
|
|
def get_parser(self, prog_name):
|
|
parser = super(NetworkList, self).get_parser(prog_name)
|
|
parser.add_argument(
|
|
'container',
|
|
metavar='<container>',
|
|
help='ID or name of the container to list networks.'
|
|
)
|
|
return parser
|
|
|
|
def take_action(self, parsed_args):
|
|
client = _get_client(self, parsed_args)
|
|
opts = {}
|
|
opts['container'] = parsed_args.container
|
|
opts = zun_utils.remove_null_parms(**opts)
|
|
networks = client.containers.network_list(**opts)
|
|
columns = ('net_id', 'port_id', 'fixed_ips')
|
|
return (columns, (utils.get_item_properties(
|
|
network, columns, formatters={
|
|
'fixed_ips': zun_utils.format_fixed_ips})
|
|
for network in networks))
|
|
|
|
|
|
class ActionList(command.Lister):
|
|
"""List actions on a container"""
|
|
log = logging.getLogger(__name__ + ".ListActions")
|
|
|
|
def get_parser(self, prog_name):
|
|
parser = super(ActionList, self).get_parser(prog_name)
|
|
parser.add_argument(
|
|
'container',
|
|
metavar='<container>',
|
|
help='ID or name of the container to list actions.'
|
|
)
|
|
return parser
|
|
|
|
def take_action(self, parsed_args):
|
|
client = _get_client(self, parsed_args)
|
|
container = parsed_args.container
|
|
actions = client.actions.list(container)
|
|
columns = ('user_id', 'container_uuid', 'request_id', 'action',
|
|
'message', 'start_time')
|
|
return (columns, (utils.get_item_properties(action, columns)
|
|
for action in actions))
|
|
|
|
|
|
class ActionShow(command.ShowOne):
|
|
"""Show a action"""
|
|
|
|
log = logging.getLogger(__name__ + ".ShowAction")
|
|
|
|
def get_parser(self, prog_name):
|
|
parser = super(ActionShow, self).get_parser(prog_name)
|
|
parser.add_argument(
|
|
'container',
|
|
metavar='<container>',
|
|
help='ID or name of the container to show.')
|
|
parser.add_argument(
|
|
'request_id',
|
|
metavar='<request_id>',
|
|
help='request ID of action to describe.')
|
|
return parser
|
|
|
|
def take_action(self, parsed_args):
|
|
client = _get_client(self, parsed_args)
|
|
container = parsed_args.container
|
|
request_id = parsed_args.request_id
|
|
action = client.actions.get(container, request_id)
|
|
columns = _action_columns(action)
|
|
return columns, utils.get_item_properties(action, columns)
|