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