commit
d370a51100
10 changed files with 522 additions and 0 deletions
-
6.gitignore
-
12Makefile
-
26README.md
-
11ansible.cfg
-
71demo_userns.c
-
BINdemo_userns.o
-
1hosts
-
171lxc-test.py
-
206lxc_copy.py
-
18playbook.yml
@ -0,0 +1,6 @@ |
|||||
|
# ignore vim swap files |
||||
|
.*.sw? |
||||
|
|
||||
|
# ignore generated stuff |
||||
|
demo_userns |
||||
|
create_user_cgroup |
||||
@ -0,0 +1,12 @@ |
|||||
|
PROGRAMS = demo_userns |
||||
|
|
||||
|
demo_userns_OBJS = demo_userns.o |
||||
|
OBJECTS = $(demo_userns_OBJS) |
||||
|
|
||||
|
CFLAGS = -O0 -ggdb -Wall -Werror |
||||
|
|
||||
|
$(PROGRAMS): $(OBJECTS) |
||||
|
$(CC) $(LDFLAGS) -lcap -o $@ $($@_OBJS) |
||||
|
|
||||
|
%.o: %.c |
||||
|
$(CC) $(CFLAGS) -c -o $@ $< |
||||
@ -0,0 +1,26 @@ |
|||||
|
# LXC-Coding |
||||
|
|
||||
|
Playground for user namespaces and other Linux Container stuff. |
||||
|
|
||||
|
## Requirements |
||||
|
|
||||
|
A proper kernel... |
||||
|
|
||||
|
## License |
||||
|
|
||||
|
> This program is free software: you can redistribute it and/or modify |
||||
|
> it under the terms of the GNU General Public License as published by |
||||
|
> the Free Software Foundation, either version 3 of the License, or |
||||
|
> (at your option) any later version. |
||||
|
> |
||||
|
> This program is distributed in the hope that it will be useful, |
||||
|
> but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
> MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
> GNU General Public License for more details. |
||||
|
> |
||||
|
> You should have received a copy of the GNU General Public License |
||||
|
> along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
|
|
||||
|
## Author |
||||
|
|
||||
|
Georg Hopp <<georg@steffers.org>> |
||||
@ -0,0 +1,11 @@ |
|||||
|
[defaults] |
||||
|
inventory = hosts |
||||
|
host_key_checking = False |
||||
|
nocows=0 |
||||
|
|
||||
|
ansible_managed = Ansible managed: {file} on {host} |
||||
|
|
||||
|
# # do redis facts caching |
||||
|
# gathering = smart |
||||
|
# fact_caching = redis |
||||
|
# fact_caching_timeout = 86400 |
||||
@ -0,0 +1,71 @@ |
|||||
|
/* |
||||
|
* demo_userns.c |
||||
|
* |
||||
|
* Copyright 2013, Michael Kerrisk |
||||
|
* Licensed under GNU General Public License v2 or later |
||||
|
* |
||||
|
* Demonstrate the use of the clone() CLONE_NEWUSER flag. |
||||
|
* |
||||
|
* Link with "-lcap" and make sure that the "libcap-devel" (or |
||||
|
* similar) package is installed on the system. |
||||
|
*/ |
||||
|
|
||||
|
#define _GNU_SOURCE |
||||
|
|
||||
|
#include <sys/capability.h> |
||||
|
#include <sys/wait.h> |
||||
|
#include <sched.h> |
||||
|
#include <stdio.h> |
||||
|
#include <stdlib.h> |
||||
|
#include <unistd.h> |
||||
|
|
||||
|
#define errExit(msg) do { perror(msg); exit(EXIT_FAILURE); \ |
||||
|
} while (0) |
||||
|
|
||||
|
static int /* Startup function for cloned child */ |
||||
|
childFunc(void *arg) |
||||
|
{ |
||||
|
cap_t caps; |
||||
|
|
||||
|
for (;;) { |
||||
|
printf("eUID = %ld; eGID = %ld; ", |
||||
|
(long) geteuid(), (long) getegid()); |
||||
|
|
||||
|
caps = cap_get_proc(); |
||||
|
printf("capabilities: %s\n", cap_to_text(caps, NULL)); |
||||
|
|
||||
|
if (arg == NULL) |
||||
|
break; |
||||
|
|
||||
|
sleep(5); |
||||
|
} |
||||
|
|
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
#define STACK_SIZE (1024 * 1024) |
||||
|
|
||||
|
static char child_stack[STACK_SIZE]; /* Space for child's stack */ |
||||
|
|
||||
|
int |
||||
|
main(int argc, char *argv[]) |
||||
|
{ |
||||
|
pid_t pid; |
||||
|
|
||||
|
/* Create child; child commences execution in childFunc() */ |
||||
|
|
||||
|
pid = clone(childFunc, child_stack + STACK_SIZE, /* Assume stack |
||||
|
grows downward */ |
||||
|
CLONE_NEWUSER | SIGCHLD, argv[1]); |
||||
|
if (pid == -1) |
||||
|
errExit("clone"); |
||||
|
|
||||
|
/* Parent falls through to here. Wait for child. */ |
||||
|
|
||||
|
if (waitpid(pid, NULL, 0) == -1) |
||||
|
errExit("waitpid"); |
||||
|
|
||||
|
exit(EXIT_SUCCESS); |
||||
|
} |
||||
|
|
||||
|
// vim: ft=c cindent ts=4 sw=4 sts=4: |
||||
@ -0,0 +1 @@ |
|||||
|
localhost ansible_connection=local |
||||
@ -0,0 +1,171 @@ |
|||||
|
import lxc |
||||
|
import os |
||||
|
import sys |
||||
|
import hashlib |
||||
|
import json |
||||
|
import getpass |
||||
|
import subprocess |
||||
|
|
||||
|
scriptpath = os.path.dirname(os.path.realpath(__file__)) |
||||
|
|
||||
|
def lxc_start(cont): |
||||
|
if (os.getuid() != 0): |
||||
|
uname = getpass.getuser() |
||||
|
subprocess.call([scriptpath + '/create_user_cgroup']); |
||||
|
for i in os.listdir('/sys/fs/cgroup'): |
||||
|
if (i == 'openrc'): |
||||
|
continue |
||||
|
with open('/sys/fs/cgroup/'+i+'/'+uname+'/tasks', 'a') as f: |
||||
|
f.write(str(os.getpid())+'\n'); |
||||
|
|
||||
|
cont.start(); |
||||
|
|
||||
|
def basic(cont): |
||||
|
def work(data): |
||||
|
print data |
||||
|
sys.stdout.flush() |
||||
|
sys.exit(0) |
||||
|
|
||||
|
lxc_read, lxc_write = os.pipe() |
||||
|
|
||||
|
cont.attach_wait(work, 'testtest', stdout=lxc_write) |
||||
|
os.close(lxc_write) |
||||
|
|
||||
|
lxc_readfd = os.fdopen(lxc_read) |
||||
|
data = lxc_readfd.readline().rstrip('\n') |
||||
|
|
||||
|
lxc_readfd.close() |
||||
|
|
||||
|
print data |
||||
|
|
||||
|
def basic2(cont): |
||||
|
def work(data): |
||||
|
print data |
||||
|
sys.stdout.flush() |
||||
|
sys.exit(0) |
||||
|
|
||||
|
lxc_read, lxc_write = os.pipe() |
||||
|
|
||||
|
cont.attach(work, 'testtest', stdout=lxc_write) |
||||
|
os.close(lxc_write) |
||||
|
|
||||
|
lxc_readfd = os.fdopen(lxc_read) |
||||
|
data = lxc_readfd.readline().rstrip('\n') |
||||
|
|
||||
|
lxc_readfd.close() |
||||
|
|
||||
|
print data |
||||
|
|
||||
|
def copy(cont, src, dest): |
||||
|
def work(dest): |
||||
|
infno = sys.stdin.fileno() |
||||
|
data = os.read(infno, 100) |
||||
|
print(data) |
||||
|
|
||||
|
lxc_read, lxc_write = os.pipe() |
||||
|
|
||||
|
pid = cont.attach(work, 'foo', stdin=lxc_read) |
||||
|
os.close(lxc_read) |
||||
|
|
||||
|
os.write(lxc_write, 'fooooobadooooo') |
||||
|
|
||||
|
os.close(lxc_write) |
||||
|
os.waitpid(pid, 0) |
||||
|
|
||||
|
def copy2(cont, src, dest): |
||||
|
bufsize = 2048 |
||||
|
|
||||
|
def work(dest): |
||||
|
infno = sys.stdin.fileno() |
||||
|
with open(dest, 'w') as outfile: |
||||
|
data = os.read(infno, bufsize) |
||||
|
while (bufsize == len(data)): |
||||
|
outfile.write(data) |
||||
|
data = os.read(infno, bufsize) |
||||
|
outfile.write(data) |
||||
|
|
||||
|
lxc_read, lxc_write = os.pipe() |
||||
|
|
||||
|
pid = cont.attach(work, dest, stdin=lxc_read) |
||||
|
os.close(lxc_read) |
||||
|
|
||||
|
with open(src, 'r') as infile: |
||||
|
data = infile.read(bufsize) |
||||
|
while (bufsize == len(data)): |
||||
|
os.write(lxc_write, data) |
||||
|
data = infile.read(bufsize) |
||||
|
os.write(lxc_write, data) |
||||
|
|
||||
|
os.close(lxc_write) |
||||
|
os.waitpid(pid, 0) |
||||
|
|
||||
|
def verify(cont, src, dest): |
||||
|
bufsize = 2048 |
||||
|
|
||||
|
def work(dest): |
||||
|
digest = '' |
||||
|
try: |
||||
|
chk = hashlib.sha1() |
||||
|
with open(dest, 'r') as dfile: |
||||
|
data = dfile.read(bufsize) |
||||
|
while(bufsize == len(data)): |
||||
|
chk.update(data) |
||||
|
data = dfile.read(bufsize) |
||||
|
chk.update(data) |
||||
|
digest = chk.hexdigest() |
||||
|
except: |
||||
|
pass |
||||
|
sys.stdout.write(digest) |
||||
|
sys.stdout.flush() |
||||
|
|
||||
|
lxc_read, lxc_write = os.pipe() |
||||
|
|
||||
|
pid = cont.attach(work, dest, stdout=lxc_write) |
||||
|
os.close(lxc_write) |
||||
|
|
||||
|
chk = hashlib.sha1() |
||||
|
with open(src, 'r') as dfile: |
||||
|
data = dfile.read(bufsize) |
||||
|
while (bufsize == len(data)): |
||||
|
chk.update(data) |
||||
|
data = dfile.read(bufsize) |
||||
|
chk.update(data) |
||||
|
|
||||
|
lxc_chk = os.read(lxc_read, 100) |
||||
|
os.close(lxc_read) |
||||
|
|
||||
|
return json.dumps({ |
||||
|
'equal': lxc_chk == chk.hexdigest(), |
||||
|
'local_sum': chk.hexdigest(), |
||||
|
'container_sum': lxc_chk}) |
||||
|
|
||||
|
cont = lxc.Container('ansible_test') |
||||
|
|
||||
|
assert cont.defined |
||||
|
|
||||
|
started = False |
||||
|
|
||||
|
if not cont.running: |
||||
|
lxc_start(cont) |
||||
|
if not cont.wait('RUNNING', timeout=5): |
||||
|
raise EnvironmentError(0x01, '[lxc] unable to start container', cont) |
||||
|
started = True |
||||
|
|
||||
|
#basic(cont) |
||||
|
#basic2(cont) |
||||
|
#copy(cont, 'verkauf.jpg', '/tmp/foo') |
||||
|
|
||||
|
print __file__ |
||||
|
print os.path.realpath(__file__) |
||||
|
print os.path.dirname(os.path.realpath(__file__)) |
||||
|
|
||||
|
vres = json.loads(verify(cont, 'verkauf.jpg', '/tmp/foo')) |
||||
|
if not vres['equal']: |
||||
|
print "copy file" |
||||
|
copy2(cont, 'verkauf.jpg', '/tmp/foo') |
||||
|
else: |
||||
|
print "files are equal" |
||||
|
|
||||
|
if started: |
||||
|
cont.shutdown(5) |
||||
|
|
||||
@ -0,0 +1,206 @@ |
|||||
|
#!/usr/bin/python |
||||
|
# |
||||
|
|
||||
|
# (c) 2014, Pavel Antonov <antonov@adwz.ru> |
||||
|
# |
||||
|
# This file is part of Ansible |
||||
|
# |
||||
|
# This module is free software: you can redistribute it and/or modify |
||||
|
# it under the terms of the GNU General Public License as published by |
||||
|
# the Free Software Foundation, either version 3 of the License, or |
||||
|
# (at your option) any later version. |
||||
|
# |
||||
|
# This software is distributed in the hope that it will be useful, |
||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
# GNU General Public License for more details. |
||||
|
# |
||||
|
# You should have received a copy of the GNU General Public License |
||||
|
# along with this software. If not, see <http://www.gnu.org/licenses/>. |
||||
|
|
||||
|
###################################################################### |
||||
|
|
||||
|
DOCUMENTATION = ''' |
||||
|
--- |
||||
|
module: lxc_copy |
||||
|
author: Georg Hopp |
||||
|
version_added: "1.9" |
||||
|
short_description: copy files in an lxc |
||||
|
description: |
||||
|
- Very simple first start to copy files from host to lxc container |
||||
|
even in non privileged containers |
||||
|
options: |
||||
|
name: |
||||
|
description: |
||||
|
- Lxc container name to use |
||||
|
required: true |
||||
|
default: null |
||||
|
aliases: [] |
||||
|
src: |
||||
|
description: |
||||
|
- Path to source file |
||||
|
required: true |
||||
|
default: null |
||||
|
aliases: [] |
||||
|
dest: |
||||
|
description: |
||||
|
- Path to file in container |
||||
|
required: true |
||||
|
default: null |
||||
|
aliases: [] |
||||
|
requirements: [ "lxc", "create_user_cgroup" ] |
||||
|
''' |
||||
|
|
||||
|
EXAMPLES = ''' |
||||
|
Copy a file from the host in the container: |
||||
|
|
||||
|
- hosts: localhost |
||||
|
tasks: |
||||
|
- name: copy host file to container |
||||
|
lxc_copy: |
||||
|
name: ansible_test |
||||
|
src: nicefile |
||||
|
dest: /tmp/verynicefile |
||||
|
''' |
||||
|
|
||||
|
try: |
||||
|
import lxc |
||||
|
import sys |
||||
|
import os |
||||
|
import json |
||||
|
import getpass |
||||
|
import hashlib |
||||
|
from contextlib import contextmanager |
||||
|
except ImportError, e: |
||||
|
print "failed=True msg='failed to import python module: %s'" % e |
||||
|
sys.exit(1) |
||||
|
|
||||
|
class LxcManager: |
||||
|
@contextmanager |
||||
|
def lxc_started(self): |
||||
|
started = self.container.running |
||||
|
|
||||
|
if not started and not self.container.running: |
||||
|
if (os.getuid() != 0): |
||||
|
uname = getpass.getuser() |
||||
|
subprocess.call(["create_user_cgroup"]); |
||||
|
for i in os.listdir('/sys/fs/cgroup'): |
||||
|
if (i == 'openrc'): |
||||
|
continue |
||||
|
with open('/sys/fs/cgroup/'+i+'/'+uname+'/tasks', 'a') as f: |
||||
|
f.write(str(os.getpid())+'\n'); |
||||
|
|
||||
|
self.container.start(); |
||||
|
|
||||
|
yield self |
||||
|
|
||||
|
if not started and self.container.running: |
||||
|
self.container.shutdown(5) |
||||
|
|
||||
|
def __init__(self, module): |
||||
|
self.module = module |
||||
|
self.name = self.module.params.get('name') |
||||
|
self.src = self.module.params.get('src') |
||||
|
self.dest = self.module.params.get('dest') |
||||
|
self.container = lxc.Container(self.name) |
||||
|
self.changed = False |
||||
|
self.log = [] |
||||
|
self.error_msg = None |
||||
|
self.buffer_size = 20480 |
||||
|
|
||||
|
def get_log(self, as_string=True): |
||||
|
return "".join(self.log) if as_string else self.log |
||||
|
|
||||
|
def _verify(self): |
||||
|
def work(dest): |
||||
|
digest = '' |
||||
|
try: |
||||
|
chk = hashlib.sha1() |
||||
|
with open(dest, 'r') as dfile: |
||||
|
data = dfile.read(self.buffer_size) |
||||
|
while(self.buffer_size == len(data)): |
||||
|
chk.update(data) |
||||
|
data = dfile.read(self.buffer_size) |
||||
|
chk.update(data) |
||||
|
digest = chk.hexdigest() |
||||
|
except: |
||||
|
pass |
||||
|
sys.stdout.write(digest) |
||||
|
sys.stdout.flush() |
||||
|
|
||||
|
lxc_read, lxc_write = os.pipe() |
||||
|
|
||||
|
pid = self.container.attach(work, self.dest, stdout=lxc_write) |
||||
|
os.close(lxc_write) |
||||
|
|
||||
|
chk = hashlib.sha1() |
||||
|
with open(self.src, 'r') as dfile: |
||||
|
data = dfile.read(self.buffer_size) |
||||
|
while (self.buffer_size == len(data)): |
||||
|
chk.update(data) |
||||
|
data = dfile.read(self.buffer_size) |
||||
|
chk.update(data) |
||||
|
|
||||
|
lxc_chk = os.read(lxc_read, 100) # read digest from container.... |
||||
|
os.close(lxc_read) |
||||
|
|
||||
|
self.files_equal = lxc_chk == chk.hexdigest() |
||||
|
self.local_sum = chk.hexdigest() |
||||
|
self.container_sum = lxc_chk |
||||
|
|
||||
|
def copy(self): |
||||
|
with self.lxc_started(): |
||||
|
self._verify() |
||||
|
if not self.files_equal: |
||||
|
def work(dest): |
||||
|
infno = sys.stdin.fileno() |
||||
|
with open(dest, 'w') as outfile: |
||||
|
data = os.read(infno, self.buffer_size) |
||||
|
while (self.buffer_size == len(data)): |
||||
|
outfile.write(data) |
||||
|
data = os.read(infno, self.buffer_size) |
||||
|
outfile.write(data) |
||||
|
|
||||
|
lxc_read, lxc_write = os.pipe() |
||||
|
|
||||
|
pid = self.container.attach(work, self.dest, stdin=lxc_read) |
||||
|
os.close(lxc_read) |
||||
|
|
||||
|
with open(self.src, 'r') as infile: |
||||
|
data = infile.read(self.buffer_size) |
||||
|
while (self.buffer_size == len(data)): |
||||
|
os.write(lxc_write, data) |
||||
|
data = infile.read(self.buffer_size) |
||||
|
os.write(lxc_write, data) |
||||
|
|
||||
|
os.close(lxc_write) |
||||
|
os.waitpid(pid, 0) |
||||
|
self.changed = True |
||||
|
|
||||
|
def has_changed(self): |
||||
|
return self.changed |
||||
|
|
||||
|
|
||||
|
def main(): |
||||
|
module = AnsibleModule( |
||||
|
argument_spec = dict( |
||||
|
name = dict(required=True, default=None), |
||||
|
src = dict(required=True, default=None), |
||||
|
dest = dict(required=True, default=None), |
||||
|
) |
||||
|
) |
||||
|
|
||||
|
manager = LxcManager(module) |
||||
|
|
||||
|
image_id = None |
||||
|
msg = '' |
||||
|
do_build = False |
||||
|
|
||||
|
manager.copy() |
||||
|
|
||||
|
module.exit_json(failed=False, changed=manager.has_changed(), msg="File copied") |
||||
|
|
||||
|
# import module snippets |
||||
|
from ansible.module_utils.basic import * |
||||
|
if __name__ == '__main__': |
||||
|
main() |
||||
@ -0,0 +1,18 @@ |
|||||
|
--- |
||||
|
- name: test playbook for lxc_copy |
||||
|
hosts: all |
||||
|
tasks: |
||||
|
- name: copy single file in container |
||||
|
lxc_copy: |
||||
|
args: |
||||
|
name: ansible_test |
||||
|
src: files/verkauf.jpg |
||||
|
dest: /tmp/foo |
||||
|
|
||||
|
- name: copy files in container |
||||
|
lxc_copy: |
||||
|
args: |
||||
|
name: ansible_test |
||||
|
src: files/{{ item }} |
||||
|
dest: /tmp/{{ item }} |
||||
|
with_items: ['a', 'b', 'c', 'd', 'e', 'f', 'g'] |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue