fuel-dev-tools/fuel_dev_tools/docker/__init__.py

285 lines
7.4 KiB
Python

# Copyright 2015 Mirantis, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import json
import logging
import os
import six
from fuel_dev_tools import command
from fuel_dev_tools import exc
from fuel_dev_tools import ssh
DOCKER_CONTAINER_PATH = '/var/lib/docker/containers/'
DOCKER_DEVICEMAPPER_PATH = '/var/lib/docker/devicemapper/mnt/'
class DockerMixin(ssh.SSHMixin):
container = None
default_command = None
def get_log_directory(self):
return ''
def container_command(self, *commands):
return [
# 'lxc-attach', '--name', self.get_full_docker_id()
'docker', 'exec', self.get_docker_id()
] + list(commands)
def container_command_interactive(self, *commands):
return [
# 'lxc-attach', '--name', self.get_full_docker_id()
'docker', 'exec', '-it', self.get_docker_id()
] + list(commands)
def get_container_config(self):
d = self.get_container_config_directory()
config = self.ssh_command('cat %s/config.json' % d).decode('utf-8')
return json.loads(config)
def get_container_config_directory(self):
iid = self.get_docker_id()
paths = self.ssh_command(
'ls %s | grep %s' % (DOCKER_CONTAINER_PATH, iid)
).decode('utf-8')
return os.path.join(DOCKER_CONTAINER_PATH, paths.split()[0])
def get_container_directory(self):
iid = self.get_docker_id()
if not iid:
raise exc.DockerError('Docker ID could not be fetched')
paths = self.ssh_command(
'ls %s | grep %s' % (DOCKER_DEVICEMAPPER_PATH, iid)
).decode('utf-8')
return os.path.join(DOCKER_DEVICEMAPPER_PATH, paths.split()[0])
def get_docker_id(self, get_exited=False):
"""Returns first 12 characters of LXC container ID.
(as returned by the 'docker ps' command)
:param get_exited:
:return:
"""
up = self.ssh_command(
'docker ps -a | grep -i %s | grep Up | cut -f 1 -d " "' %
self.container
).decode('utf-8')
self.print_debug('FOUND CONTAINERS: %r' % up)
if not up and get_exited:
self.print_debug('Container not Up, trying Exited')
up = self.ssh_command(
'docker ps -a | grep -i %s | grep Exited | cut -f 1 -d " "' %
self.container
).decode('utf-8')
self.print_debug('FOUND CONTAINERS: %r' % up)
if not up:
raise exc.DockerError(
"Container '%s' not found or not functional" %
self.container
)
return up
def get_full_docker_id(self, get_exited=False):
"""Returns full container ID.
:return:
"""
iid = self.get_docker_id(get_exited=get_exited)
iid = self.ssh_command(
"docker inspect -f '{{.Id}}' %s" % iid
).decode('utf-8').strip()
return iid
def get_log_files(self, args):
log_dir = self.get_log_directory()
files = '*.log'
if args.files:
if len(args.files) == 1:
files = '%s.log' % args.files[0]
else:
files = '{%s}.log' % ','.join(args.files)
return os.path.join(log_dir, files)
def restart_container(self):
result = self.ssh_command(
'docker restart %s' % self.get_docker_id()
)
self.print_debug(result)
def start_container(self):
result = self.ssh_command(
'docker start %s' % self.get_docker_id(get_exited=True)
)
self.print_debug(result)
def stop_container(self):
result = self.ssh_command(
'docker stop %s' % self.get_docker_id()
)
self.print_debug(result)
class IdCommand(command.BaseCommand):
def take_action(self, parsed_args):
six.print_(self.get_docker_id(get_exited=True))
class ConfigCommand(command.BaseCommand):
def take_action(self, parsed_args):
six.print_(json.dumps(self.get_container_config(), indent=2))
class DirCommand(command.BaseCommand):
def take_action(self, parsed_args):
six.print_(self.get_container_directory())
class LogCommand(command.BaseCommand):
def get_log_directory(self):
raise NotImplementedError('No log directory for this command')
def get_parser(self, prog_name):
parser = super(LogCommand, self).get_parser(prog_name)
parser.add_argument(
'files',
type=str,
nargs='*',
help='List of files to show (all by default).'
)
return parser
def take_action(self, parsed_args):
six.print_(
self.ssh_command(
'tail', '-n', '100000', self.get_log_files(parsed_args)
)
)
class RestartCommand(command.BaseCommand):
def take_action(self, parsed_args):
self.restart_container()
class RsyncCommand(command.RsyncCommand):
@property
def base_target_dir(self):
return os.path.join(
self.get_container_directory(),
'rootfs'
)
class ShellCommand(command.BaseCommand):
default_command = None
log = logging.getLogger(__name__)
def get_parser(self, prog_name):
parser = super(ShellCommand, self).get_parser(prog_name)
help_msg = 'Command to execute'
if self.default_command:
help_msg = '%s (default: %s)' % (help_msg, self.default_command)
parser.add_argument(
'-c', '--command',
default=None,
help=help_msg
)
return parser
def take_action(self, parsed_args):
command = parsed_args.command
if not command:
command = self.default_command
if not command:
command = '/bin/bash'
return self.ssh_command_interactive(
*self.container_command_interactive(command)
)
class StartCommand(command.BaseCommand):
def take_action(self, parsed_args):
self.start_container()
class StopCommand(command.BaseCommand):
def take_action(self, parsed_args):
self.stop_container()
class TailCommand(command.BaseCommand):
def get_log_directory(self):
raise NotImplementedError('No log directory for this command')
def get_parser(self, prog_name):
parser = super(TailCommand, self).get_parser(prog_name)
parser.add_argument(
'files',
type=str,
nargs='*',
help='List of files to show (all by default).'
)
return parser
def take_action(self, parsed_args):
self.ssh_command_interactive(
'tail', '-F', self.get_log_files(parsed_args)
)
class VolumesCommand(command.BaseCommand):
def take_action(self, parsed_args):
six.print_(
json.dumps(
self.get_container_config().get('Volumes', {}), indent=2
)
)