File: container.py

package info (click to toggle)
libcgroup 3.1.0-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 6,764 kB
  • sloc: ansic: 14,997; cpp: 9,957; python: 8,340; sh: 5,194; yacc: 470; makefile: 400; lex: 38
file content (221 lines) | stat: -rw-r--r-- 5,895 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
# SPDX-License-Identifier: LGPL-2.1-only
#
# Container class for the libcgroup functional tests
#
# Copyright (c) 2019-2021 Oracle and/or its affiliates.
# Author: Tom Hromatka <tom.hromatka@oracle.com>
#

from run import Run, RunError
from queue import Queue
import threading as tp
from log import Log
import consts
import time
import os


class Container(object):
    def __init__(self, name, stop_timeout=None, arch=None, cfg_path=None,
                 distro=None, release=None):
        self.name = name
        self.privileged = True

        if stop_timeout:
            self.stop_timeout = stop_timeout
        else:
            self.stop_timeout = consts.DEFAULT_CONTAINER_STOP_TIMEOUT

        if arch:
            self.arch = arch
        else:
            self.arch = consts.DEFAULT_CONTAINER_ARCH

        if distro:
            self.distro = distro
        else:
            self.distro = consts.DEFAULT_CONTAINER_DISTRO

        if release:
            self.release = release
        else:
            self.release = consts.DEFAULT_CONTAINER_RELEASE

        ftest_dir = os.path.dirname(os.path.abspath(__file__))
        tests_dir = os.path.dirname(ftest_dir)
        # save off the path to the libcgroup source code
        self.libcg_dir = os.path.dirname(tests_dir)

    def __str__(self):
        out_str = 'Container {}'.format(self.name)
        out_str += '\n\tdistro = {}'.format(self.distro)
        out_str += '\n\trelease = {}'.format(self.release)
        out_str += '\n\tarch = {}'.format(self.arch)
        out_str += '\n\tstop_timeout = {}\n'.format(self.stop_timeout)

        return out_str

    # configure the container to meet our needs
    def config(self):
        # map our UID and GID to the same UID/GID in the container
        cmd = (
                    'printf "uid {} 1000\ngid {} 1000" | sudo lxc config set '
                    '{} raw.idmap -'
                    ''.format(os.getuid(), os.getgid(), self.name)
              )
        Run.run(cmd, shell_bool=True)

        # add the libcgroup root directory (where we did the build) into
        # the container
        cmd2 = list()
        if self.privileged:
            cmd2.append('sudo')
        cmd2.append('lxc')
        cmd2.append('config')
        cmd2.append('device')
        cmd2.append('add')
        cmd2.append(self.name)
        cmd2.append('libcgsrc')  # arbitrary name of device
        cmd2.append('disk')
        # to appease gcov, mount the libcgroup source at the same path as we
        # built it.  This can be worked around someday by using
        # GCOV_PREFIX_STRIP, but that was more difficult to setup than just
        # doing this initially
        cmd2.append('source={}'.format(self.libcg_dir))
        cmd2.append('path={}'.format(self.libcg_dir))

        return Run.run(cmd2)

    def _init_container(self, q):
        cmd = list()

        if self.privileged:
            cmd.append('sudo')

        cmd.append('lxc')
        cmd.append('init')

        cmd.append('{}:{}'.format(self.distro, self.release))

        cmd.append(self.name)

        try:
            Run.run(cmd)
            q.put(True)
        except Exception:  # noqa: E722
            q.put(False)
        except BaseException:  # noqa: E722
            q.put(False)

    def create(self):
        # Github Actions sometimes has timeout issues with the LXC sockets.
        # Try this command multiple times in an attempt to work around this
        # limitation

        queue = Queue()
        sleep_time = 5
        ret = False

        for i in range(5):
            thread = tp.Thread(target=self._init_container, args=(queue, ))
            thread.start()

            time_cnt = 0
            while thread.is_alive():
                time.sleep(sleep_time)
                time_cnt += sleep_time
                Log.log_debug('Waiting... {}'.format(time_cnt))

            ret = queue.get()
            if ret:
                break
            else:
                try:
                    self.delete()
                except RunError:
                    pass

            thread.join()

        if not ret:
            raise ContainerError('Failed to create the container')

    def delete(self):
        cmd = list()

        if self.privileged:
            cmd.append('sudo')

        cmd.append('lxc')
        cmd.append('delete')

        cmd.append(self.name)

        return Run.run(cmd)

    def run(self, cntnr_cmd, shell_bool=False):
        cmd = list()

        if self.privileged:
            cmd.append('sudo')

        cmd.append('lxc')
        cmd.append('exec')

        cmd.append(self.name)

        cmd.append('--')

        # concatenate the lxc exec command with the command to be run
        # inside the container
        if isinstance(cntnr_cmd, str):
            cmd.append(cntnr_cmd)
        elif isinstance(cntnr_cmd, list):
            cmd = cmd + cntnr_cmd
        else:
            raise ContainerError('Unsupported command type')

        return Run.run(cmd, shell_bool=shell_bool)

    def start(self):
        cmd = list()

        if self.privileged:
            cmd.append('sudo')

        cmd.append('lxc')
        cmd.append('start')

        cmd.append(self.name)

        return Run.run(cmd)

    def stop(self, force=True):
        cmd = list()

        if self.privileged:
            cmd.append('sudo')

        cmd.append('lxc')
        cmd.append('stop')

        cmd.append(self.name)

        if force:
            cmd.append('-f')
        else:
            cmd.append('--timeout')
            cmd.append(str(self.stop_timeout))

        return Run.run(cmd)


class ContainerError(Exception):
    def __init__(self, message):
        super(ContainerError, self).__init__(message)

    def __str__(self):
        out_str = 'ContainerError:\n\tmessage = {}'.format(self.message)
        return out_str

# vim: set et ts=4 sw=4: