def get_vm_info(): cmd = 'ps ax -o "pid args" | grep "/bin/kvm" | grep "/var/lib/buildkit/vm"' result = stacks.process( cmd=cmd, shell = True, ) lines = result.stdout.split('\n') instances = [] for line in lines: if not line.strip() or cmd in line: continue parts = [part for part in line.strip().split(' ') if part] pid = parts[0] try: image_arg = parts[18].split('=')[1].split(',')[0].split('/')[-2] except: image_arg = 'ERROR' try: net_arg = parts[22].split(',')[1].split('=')[1] except: net_arg = 'ERROR' instance = stacks.obj( pid = pid, image = image_arg, tunnel = net_arg ) instances.append(instance) return instances
def umount_image(path): result = stacks.process( 'umount %s'%path, shell=True, ) if result.retcode: raise Exception('Could not unmount the path %s. %s\n'%(path, result.stderr))
def create_tunnel(tunnel): cmd = '''\ modprobe tun tunctl -b -u root -t %(tunnel)s brctl addif br0 %(tunnel)s ifconfig %(tunnel)s up 0.0.0.0 promisc'''%dict(tunnel=tunnel) result = stacks.process(cmd, shell=True, echo=True, merge=True) return result
def create_bridge(interface, bridge_name, add=False): cmd = "iptables -t nat -A POSTROUTING -o %s -j MASQUERADE" % interface if add: cmd += '\nbrctl addbr %s'%bridge_name cmd += '\nifconfig %s 192.168.100.254 netmask 255.255.255.0 up'%bridge_name cmd += '\necho "1" > /proc/sys/net/ipv4/ip_forward' result = stacks.process(cmd, shell=True, echo=True, merge=True) return result
def determine_interface(ip): result = stacks.process( "/sbin/ifconfig | grep -B 1 '%s' | grep -v '%s' | awk '{ print $1}'"%(ip, ip), shell=True, ) if result.retcode or result.stderr: raise Exception('Could not determine the interface automatically, please use the --interface option') interface = result.stdout.strip() return interface
def gen_mac(): result = stacks.process( r"dd if=/dev/urandom count=1 2>/dev/null | md5sum | sed 's/^\(..\)\(..\)\(..\)\(..\).*$/\1:\2:\3:\4/'", shell=True, ) if result.retcode or result.stderr: raise Exception('Could not generate a mac address automatically, please use the --mac-address option') mac = '52:54:'+result.stdout.strip() return mac
def start_apt_cacher_ng_if_not_running(): cmd = '''\ APTCACHERNG=`ps aux | grep /usr/sbin/apt-cacher | grep -v grep` if [ ! "$APTCACHERNG" ] ; then echo "Starting apt-cacher-ng ..." /etc/init.d/apt-cacher-ng start echo "done." fi ''' result = stacks.process(cmd, shell=True, echo=True) return result
def determine_ip(): result = stacks.process( "/sbin/ifconfig $NETWORK_DEVICE | grep 'inet addr:' | cut -d: -f2 | awk '{ print $1}' | grep -v '127.0.0.1' | grep -v '192.168.100.' | grep -v '192.168.122.1'", shell=True ) exception = Exception('Could not determine the IP address, is the host connected to the internet? If so, please --ip option to specify the IP address manually') if result.retcode or result.stderr: raise exception ip = result.stdout.strip().split('\n')[0] if not valid_ip(ip): raise exception return ip
def clone(src, dst): if '/' in src: raise Exception('The src argument cannot contain a / character') if '/' in dst: raise Exception('The dst argument cannot contain a / character') result = stacks.process( [ 'sudo', '-u', 'buildkit', 'cp', '-pr', '/var/lib/buildkit/vm/%s'%src, '/var/lib/buildkit/vm/%s'%dst, ], ) return result
def run(cmd): if os.geteuid() != 0: cmd.err("This script must be run as root") return 1 result = full_info([cmd.args[0]]) if result.error: cmd.err(result.error) return 2 serial = result.instances[0].serial cmd.out('Connecting to %r ...', serial) result = stacks.process( [ 'socat', '-', serial, ], in_data = '\n\n', )
def mount_image(image, path): if not os.path.exists(path): os.mkdir(path) uid = pwd.getpwnam('buildkit')[2] gid = grp.getgrnam('buildkit')[2] os.chown(path, uid, gid) files = os.listdir(path) if files: if len(files) > 1: # Must be mounted raise Exception('The VM already appears to be mounted at %r'%path) umount_image(path) elif len(files) > 0 and files[0] == 'vm.info': os.remove(os.path.join(path, 'vm.info')) else: raise Exception('The %r directory is not empty'%path) result = stacks.process( 'mount -t ext4 -o loop,offset=512 %s %s'%(image, path), shell=True, ) if result.retcode: raise Exception('Could not mount the image %s. %s\n'%(image, result.stderr))
def get_bridge_info(): result = stacks.process( [ 'brctl', 'show', ], ) lines = result.stdout.split('\n') interfaces_pos = lines[0].find('interfaces') tunnels = [] bridges = stacks.obj() bridge = None for line in lines[1:]: new_bridge = line.split('\t')[0].strip() if new_bridge: bridge = new_bridge if not bridges.has_key(bridge): bridges[bridge] = [] tunnel = line.split('\t')[-1] if tunnel: bridges[bridge].append(tunnel) tunnels.append(tunnel) tunnels.sort() return stacks.obj(bridges=bridges, tunnels=tunnels)
def run(cmd): #if cmd.opts.wait_for_boot or cmd.opts.copy_dir: # cmd.err('One of the options you\'ve chosen isn\'t implemented yet') # return 1 if os.geteuid() != 0: cmd.err("This script must be run as root") return 1 extra = [] for c in cmd.args[1:]: if c.strip() in ['-h', '--help']: cmd.err('ERROR: Help flags must be specified before the image name NAME') return 5 else: extra.append(c) if cmd.args[0] in [instance.image for instance in get_vm_info()]: cmd.err('ERROR: The image %r is already in use', cmd.args[0]) return 4 # Make sure there isn't already a version of this image running if not cmd.opts.ip: cmd.err("No IP address available") return 3 else: cmd.out("Host IP is %s", cmd.opts.ip) interface = determine_interface(cmd.opts.ip) if not interface: cmd.err("No network interface available") return 2 else: cmd.out("Network interface is %s", interface) if cmd.opts.use_apt_proxy: start_apt_cacher_ng_if_not_running() bridge_info = get_bridge_info() add = True if cmd.opts.bridge_name in bridge_info.bridges.keys(): add = False create_bridge(interface, cmd.opts.bridge_name, add=add) tunnel = cmd.opts.tunnel if tunnel is None: tunnel = gen_tunnel(cmd.opts.bridge_name) if add or tunnel not in bridge_info.bridges[cmd.opts.bridge_name]: create_tunnel(tunnel) path = "/var/lib/buildkit/vm/%s/vmtmp"%cmd.args[0] mount_image("/var/lib/buildkit/vm/%s/disk.raw"%cmd.args[0], path) update_image(cmd.args[0], cmd.opts.ip, use_apt_proxy=cmd.opts.use_apt_proxy) for spec in cmd.opts.copy_file: cmd.out('Copying %r to %r ...', spec.src, os.path.join(path, spec.dst[1:])) shutil.copy2(spec.src, os.path.join(path, spec.dst[1:])) cmd.out('done.') fp = open(os.path.join(path, 'etc/buildkit_on_start.sh'), 'w') fp.write('#!/bin/sh\n\n') if cmd.opts.exec_on_boot: fp.write(cmd.opts.exec_on_boot) fp.close() os.chmod(os.path.join(path, 'etc/buildkit_on_start.sh'), stat.S_IEXEC) umount_image(path) opts = cmd.opts.copy() opts['tunnel'] = tunnel cmd.out("Starting VM "+cmd.args[0]+" on %(tunnel)s to %(bridge_name)s with MAC %(mac_address)s ..."%opts) #import signal #class Alarm(Exception): # pass #def alarm_handler(signum, frame): # raise Alarm #signal.signal(signal.SIGALRM, alarm_handler) #signal.alarm(1) monitor = [] serial = [] def out(fh, stdin, output, exit): pass #while not monitor or not serial: # print "out(%s %s %s)\n" %(monitor, serial, exit) # line = fh.readline() # if not line: # exit.append(11) # break def err(fh, stdin, output, exit): while not monitor or not serial: #print "err(%s %s %s)\n" %(monitor, serial, exit) line = fh.readline() if not line: exit.append(11) break #import pdb; pdb.set_trace() if line.startswith('char device redirected to '): device = line[len('char device redirected to '):-1] if monitor: serial.append(device) else: monitor.append(device) else: if not cmd.opts.no_console: cmd.out(line) output.write(line) if monitor and serial: exit.append(10) if not cmd.opts.graphics: extra.append('-nographic') result = stacks.process( [ '/usr/bin/kvm', '-enable-kvm', '-M', 'pc-0.12', '-serial', 'pty', '-monitor', 'pty', #'-parallel', 'none', #'-usb' '-name', 'dev', # Memory and CPU '-m', cmd.opts.mem, '-smp', cmd.opts.cpus, # Drive options '-boot', 'c', '-drive', 'file=/var/lib/buildkit/vm/%s/disk.raw,if=ide,index=0,boot=on'%cmd.args[0], # Network options '-net', 'nic,macaddr=%s'%cmd.opts.mac_address, '-net', 'tap,ifname=%s,script=no,downscript=no'%tunnel, ] + extra, wait_for_retcode=False, out=out, err=err, ) serial = serial[0] monitor = monitor[0] # Save the serial and monitor information fp = open('/var/lib/buildkit/vm/%s/vmtmp/vm.info'%cmd.args[0], 'w') fp.write('%s\n%s\n%s'%(result.pid, serial, monitor)) fp.close() # Output some success messages cmd.out('Started successfully with pid %r'%result.pid) cmd.out('You can access the QEMU monitor for this VM like so:') cmd.out(' $ sudo screen %s', monitor) cmd.out('Once connected you should type \'Ctrl+a\' followed by \'k\' to leave the monitor. (Don\'t type \'quit\' as that will immediately exit the VM - akin to removing the power cable). If you are already running screen, you will need to type \'Ctrl+a a\' followed by k\' to quit the screen connected to %r.', serial) cmd.out('You can connect to the VM\'s serial console like this:') cmd.out(' $ sudo screen %s', serial) cmd.out('Again you can exit the console with \'Ctrl+a\' followed by \'k\' (or \'Ctrl+a a\' followed by k\' if you are already in a screen session).') if not cmd.opts.dont_wait: def errfn(fh, stdin, output, exit): pass def outfn(fh, stdin, output, exit): while not exit: line = fh.readline() if not cmd.opts.no_console: cmd.out(line, end='') output.write(line) if cmd.opts.wait_message in line: cmd.out("Found the wait message, exiting ...") exit.append(0) if not cmd.opts.no_console: cmd.out('Connecting to the VM serial console ...') result = stacks.process( [ 'socat', '-', serial, ], out=outfn, err=errfn, wait_for_retcode=False, ) cmd.out('done.')
def update_image(image, host_ip, use_apt_proxy=True): path = "/var/lib/buildkit/vm/%s/vmtmp"%image shutil.copyfile('/etc/resolv.conf', os.path.join(path, 'etc/resolv.conf')) fp =open(os.path.join(path, 'etc/apt/apt.conf'), 'w') if use_apt_proxy: fp.write('Acquire::http { Proxy \"http://%s:3142/ubuntu\"; };'%host_ip) else: fp.write('') fp.close() fp = open(os.path.join(path, 'etc/buildkit_host_ip'), 'w') fp.write(host_ip) fp.close() fp = open(os.path.join(path, 'etc/hosts'), 'r') to_write = [] found = False for line in fp.read().split('\n'): if 'host.buildkit' in line: if not found: found = True to_write.append("%s host.buildkit"%host_ip) else: to_write.append(line) if not found: to_write.append("%s host.buildkit"%host_ip) fp.close() fp = open(os.path.join(path, 'etc/hosts'), 'w') fp.write('\n'.join(to_write)) fp.close() fp = open(os.path.join(path, 'etc/init.d/buildkit_exec'), 'w') fp.write('''\ #! /bin/sh # /etc/init.d/buildkit_exec # # Carry out specific functions when asked to by the system case "$1" in start) if [ -e "/etc/buildkit_on_start.sh" ] ; then echo "Running custom buildkit on start script ..." bash /etc/buildkit_on_start.sh echo "done." fi echo "Buildkit exec_on_boot_completed" ;; stop) ;; *) echo "Usage: /etc/init.d/blah {start|stop}" exit 1 ;; esac exit 0 ''') fp.close() os.chmod(os.path.join(path, 'etc/init.d/buildkit_exec'), stat.S_IEXEC) link_path = os.path.join(path, 'etc/rc2.d/S99buildkit_exec') if os.path.lexists(link_path): os.remove(link_path) os.symlink( '../init.d/buildkit_exec', link_path, ) fp = open(os.path.join(path, 'etc/init/ttyS0.conf'), 'w') fp.write('''\ # ttyS0 - getty # # This service maintains a getty on ttyS0 from the point the system is # started until it is shut down again. start on stopped rc RUNLEVEL=[2345] stop on runlevel [!2345] respawn exec /sbin/getty -L 38400 ttyS0 vt102 ''') fp.close() fp = open(os.path.join(path, 'boot/grub/menu.lst'), 'r') data = fp.read() fp.close() if "quiet splash" in data: data = data.replace("quiet splash", "console=ttyS0,115200n8 console=tty0") fp = open(os.path.join(path, 'boot/grub/menu.lst'), 'w') fp.write(data) fp.close() # Now we need to enter a chroot and update the image result = stacks.process( [ 'chroot', path, 'update-grub', ], echo=True, merge=True, ) if result.retcode: raise Exception('Could not mount the image %s. %s\n'%(image, result.stderr))
def buildkit_apt_cache_installed(): cmd = '''dpkg -l | grep "ii buildkit-apt-cache"''' result = stacks.process(cmd, shell=True, echo=True) if result.stdout.strip(): return True return False
def run(cmd): if os.geteuid() != 0: cmd.err("This script must be run as root") return 1 vm_info = get_vm_info() vm = None for instance in vm_info: if instance.image == cmd.args[0]: vm = instance if vm is None: cmd.err('VM %r is not running'%cmd.args[0]) return 1 pts_info = parse_vm_info(vm.image) if not pts_info.pid: cmd.err('Could not obtain the monitor information for %r.'%cmd.args[0]) cmd.err('If can log in to the machine you should shut it down manually. Otherwise if you know the correct char device you can stop it like this:') cmd.err(' $ echo "system_powerdown" | sudo socat - /dev/pts/XX') cmd.err('Failing that you could forcibly kill the VMs pid %r', vm.pid) return 2 cmd.out('Sending system powerdown command ...') stacks.process( 'echo "system_powerdown" | sudo socat - %s'%pts_info.monitor, shell=True, ) cmd.out('done.') if not cmd.opts.dont_wait: serial = pts_info.serial if serial != 'UNKNOWN': def err(fh, stdin, output, exit): pass def out(fh, stdin, output, exit): while not exit: line = fh.readline() if not cmd.opts.no_console: cmd.out(line, end='') output.write(line) if 'Power down.' in line: cmd.out("Found the wait message, exiting ...") exit.append(0) if not cmd.opts.no_console: cmd.out('Connecting to the VM serial console ...') result = stacks.process( [ 'socat', '-', serial, ], out=out, err=err, ) cmd.out('done.') cmd.out('Waiting for shutdown to complete ...', end='') wait = True while wait: vm_info = get_vm_info() found = False cmd.out('.', end='') for instance in vm_info: if cmd.args[0] == instance.image: found=True break if not found: wait=False else: time.sleep(2) cmd.out('\nShutdown complete.')