[Ovirt-devel] [PATCH node] First draft of replacing some of the ovirt-config-* scripts with python equivalents.
Joey Boggs
jboggs at redhat.com
Fri Oct 22 14:38:01 UTC 2010
Putting these out for feedback and comments.
These will eventually support the new newt/python based ui for installation/configuration
storage.py functions will be moved under a class for better data portability before final version
---
scripts/ovirtfunctions.py | 672 +++++++++++++++++++++++++++++++++++++++++++++
scripts/storage.py | 451 ++++++++++++++++++++++++++++++
2 files changed, 1123 insertions(+), 0 deletions(-)
create mode 100644 scripts/ovirtfunctions.py
create mode 100644 scripts/storage.py
diff --git a/scripts/ovirtfunctions.py b/scripts/ovirtfunctions.py
new file mode 100644
index 0000000..f2b9e39
--- /dev/null
+++ b/scripts/ovirtfunctions.py
@@ -0,0 +1,672 @@
+#!/usr/bin/python
+import subprocess
+import os
+from subprocess import Popen, PIPE, STDOUT
+import tempfile
+import string
+import sys
+
+
+OVIRT_LOGFILE="/var/log/ovirt.log"
+OVIRT_TMP_LOGFILE="/tmp/ovirt.log"
+# label of the oVirt partition
+OVIRT_LABEL="OVIRT"
+# configuration values are loaded in the following order:
+# 1. /etc/sysconfig/node-config sets the default values
+# 2. /etc/default/ovirt is loaded to override defaults with karg values
+NODE_SYSCONFIG="/etc/sysconfig/node-config"
+OVIRT_DEFAULTS="/etc/default/ovirt"
+
+OVIRT_VARS = {}
+# Parse all OVIRT_* variables
+if os.path.exists(NODE_SYSCONFIG):
+ try:
+ f = open(NODE_SYSCONFIG, 'r')
+ for line in f:
+ try:
+ line = line.strip()
+ key, value = line.split("\"", 1)
+ key = key.strip("=")
+ value = value.strip("\"")
+ OVIRT_VARS[key] = value
+ except:
+ pass
+ f.close()
+ except:
+ pass
+
+f = open(OVIRT_DEFAULTS, 'r')
+for line in f:
+ try:
+ line = line.strip()
+ key, value = line.split("\"", 1)
+ key = key.strip("=")
+ value = value.strip("\"")
+ OVIRT_VARS[key] = value
+ except:
+ pass
+f.close()
+
+OVIRT_BACKUP_DIR="/var/lib/ovirt-backup"
+
+MANAGEMENT_SCRIPTS_DIR="/etc/node.d"
+
+OVIRT_CONFIG_FILES = ["/etc/sysconfig/network-scripts/ifcfg-*", \
+ "/etc/rsyslog.conf", \
+ "/etc/libvirt/libvirtd.conf", \
+ "/etc/sasl2/libvirt.conf", \
+ "/etc/libvirt/passwd.db", \
+ "/etc/passwd", \
+ "/etc/shadow", \
+ "/etc/ssh/ssh_host*_key*", \
+ "/etc/default/ovirt", \
+ "/etc/sysconfig/network", \
+ "/etc/collectd.conf", \
+ "/etc/logrotate.d/ovirt-logrotate.conf" ]
+
+def log(log_entry):
+ # placeholder for now
+ print log_entry
+
+def set_console_colors():
+ GIO_CMAP = 0x4B70
+ PIO_CMAP = 0x4B71
+
+ tty_file = open("/dev/console", "rw")
+ existing_color_array = bytearray(fcntl.ioctl(tty_file.fileno(), GIO_CMAP, b"\x00" * 48))
+ color_array = existing_color_array
+ color_array[3] = 0xde
+ color_array[4] = 0xde
+ color_array[5] = 0xde
+ color_array[6] = 0x30
+ color_array[7] = 0x30
+ color_array[8] = 0x30
+ color_array[12] = 0x38
+ color_array[13] = 0x8f
+ color_array[14] = 0xcd
+ color_array[15] = 0xea
+ color_array[16] = 0xea
+ color_array[17] = 0xea
+ color_array[18] = 0x71
+ color_array[19] = 0x71
+ color_array[20] = 0x71
+ color_array[22] = 0xff
+ color_array[23] = 0xff
+ color_array[9] = 0x52
+ color_array[10] = 0x52
+ color_array[11] = 0x52
+ fcntl.ioctl(tty_file.fileno(), PIO_CMAP, bytes(color_array))
+
+def restore_console_colors():
+ GIO_CMAP = 0x4B70
+ PIO_CMAP = 0x4B71
+ tty_file = open("/dev/console", "rw")
+ fcntl.ioctl(tty_file.fileno(), PIO_CMAP, bytes(.existing_color_array))
+
+def ovirt_store_firstboot_config():
+ # persist config for standalone
+ ovirt_store_config(OVIRT_CONFIG_FILES)
+
+# return 1 if oVirt Node is running in standalone mode
+# return 0 if oVirt Node is managed by the oVirt Server
+def is_managed():
+ return OVIRT_VARS["OVIRT_STANDALONE"]
+
+# oVirt Node in standalone mode does not try to contact the oVirt Server
+def is_standalone():
+ if is_managed:
+ return False
+ else:
+ return True
+
+# return 0 if local storage is configured
+# return 1 if local storage is not configured
+def is_local_storage_configured():
+ ret = os.system("lvs HostVG/Config >/dev/null >&1")
+ if ret > 0:
+ return False
+ return True
+
+# perform automatic local disk installation
+# when at least following boot parameters are present:
+# for networking - OVIRT_BOOTIF, management NIC
+# if other ip bootparams are not specified, IPv4 DHCP is assumed
+# for storage - OVIRT_INIT, local disk to use
+# if ovirt_vol is not specified, default volume sizes are set
+def is_auto_install():
+ if OVIRT_VARS.has_key("OVIRT_BOOTIF") and OVIRT_VARS.has_key("OVIRT_INIT"):
+ return True
+ else:
+ return False
+
+# return 0 if this is an upgrade
+# return 1 otherwise
+def is_upgrade():
+ if OVIRT_VARS.has_key("OVIRT_UPGRADE") and OVIRT_VARS["OVIRT_UPGRADE"] == 1:
+ return True
+ else:
+ return False
+
+# return 0 if booted from local disk
+# return 1 if booted from other media
+def is_booted_from_local_disk():
+ ret = os.system("grep -q /dev/HostVG/ /proc/cmdline")
+ if ret == 0:
+ return True
+ else:
+ return False
+
+# was firstboot menu already shown?
+# state is stored in persistent config partition
+def is_firstboot():
+ if not OVIRT_VARS.has_key("OVIRT_FIRSTBOOT") or OVIRT_VARS["OVIRT_FIRSTBOOT"] == 1:
+ return True
+ else:
+ return False
+
+def disable_firstboot():
+ if mount_config:
+ firstboot = augeas.Augeas(root="/")
+ firstboot.set("/files/etc/defaults/ovirt/OVIRT_FIRSTBOOT", "0")
+ firstboot.set("/files/etc/defaults/ovirt/OVIRT_INIT", '""')
+ firstboot.set("/files/etc/defaults/OVIRT_UPGRADE", "0")
+ firstboot.save()
+
+# Destroys a particular volume group and its logical volumes.
+
+def wipe_volume_group(vg):
+ files_cmd = "grep %s /proc/mounts|awk '{print $2}'|sort -r" % vg
+ files = subprocess.Popen(files_cmd, shell=True, stdout=PIPE, stderr=STDOUT)
+ files_output = files.stdout.read()
+ for file in files_output.split():
+ umount_cmd = "umount %s" % file
+ os.system(umount_cmd)
+
+
+ swap_cmd = "grep %s /proc/swaps|awk '{print $1}'" % vg
+ swap = subprocess.Popen(swap_cmd, shell=True, stdout=PIPE, stderr=STDOUT)
+ swap_output = swap.stdout.read()
+ for d in swap_output:
+ log ("Turning off %s") % d
+ os.system("swapoff %s") % d
+ vgremove_cmd = "vgremove -f %s" % vg
+ os.system(vgremove_cmd)
+
+# find_srv SERVICE PROTO
+#
+# reads DNS SRV record
+# sets SRV_HOST and SRV_PORT if DNS SRV record found, clears them if not
+# Example usage:
+# find_srv ovirt tcp
+def find_srv(srv, proto):
+ domain = subprocess.Popen("dnsdomainname 2>/dev/null", shell=True, stdout=PIPE, stderr=STDOUT)
+ domain_output = domain.stdout.read()
+ if domain_output == "localdomain":
+ domain=""
+ # FIXME dig +search does not seem to work with -t srv
+ # dnsreply=$(dig +short +search -t srv _$1._$2)
+ # This is workaround:
+ search = subprocess.Popen("grep search /etc/resolv.conf", shell=True, stdout=PIPE, stderr=STDOUT)
+ search_output = search.stdout.read()
+ search = search.replace("search ","")
+ domain_search = domain_output + search_output
+ for d in domain_search.split():
+ dig_cmd = "dig +short -t srv _%s._%s.%s" % (srv, proto,search)
+ dig = subprocess.Popen(dig_cmd, shell=True, stdout=PIPE, stderr=STDOUT)
+ dig_output = dig.stdout.read()
+ dig.poll()
+ dig_rc = dnsreply.returncode()
+ if dig_rc == 0:
+ a,b,port,host = dig_output.split("=", 4)
+ return (port, host)
+ return False
+
+def ovirt_setup_libvirtd():
+ # just to get a boot warning to shut up
+ os.system("touch /etc/resolv.conf")
+
+ # make libvirtd listen on the external interfaces
+ os.system("sed -i -e 's/^#\(LIBVIRTD_ARGS=\"--listen\"\).*/\1/' /etc/sysconfig/libvirtd")
+
+ # set up qemu daemon to allow outside VNC connections
+ os.system("sed -i -e 's/^[[:space:]]*#[[:space:]]*\(vnc_listen = \"0.0.0.0\"\).*/\1/' /etc/libvirt/qemu.conf")
+ # set up libvirtd to listen on TCP (for kerberos)
+ os.system('sed -i -e "s/^[[:space:]]*#[[:space:]]*\(listen_tcp\)\>.*/\1 = 1/" \
+ -e "s/^[[:space:]]*#[[:space:]]*\(listen_tls\)\>.*/\1 = 0/" \
+ /etc/libvirt/libvirtd.conf')
+
+ # with libvirt (0.4.0), make sure we we setup gssapi in the mech_list
+ sasl_conf="/etc/sasl2/libvirt.conf"
+ ret = os.system('grep -qE "^mech_list: gssapi %s') % sasl_conf
+ if ret > 0:
+ os.system("sed -i -e \"s/^\([[:space:]]*mech_list.*\)/#\1/\" %s") % sasl_conf
+ os.system("echo \"mech_list: gssapi\" >> %s") % sasl_conf
+
+def ovirt_setup_anyterm():
+ # configure anyterm
+ anyterm_conf = open("/etc/sysconfig/anyterm", "w")
+ anyterm_conf.write("ANYTERM_CMD=\"sudo /usr/bin/virsh console %p\"")
+ anyterm_conf.write("ANYTERM_LOCAL_ONLY=false")
+ anyterm_conf.close()
+ # permit it to run the virsh console
+ os.system("echo \"anyterm ALL=NOPASSWD: /usr/bin/virsh console *\" >> /etc/sudoers")
+
+# mount livecd media
+# e.g. CD /dev/sr0, USB /dev/sda1,
+# PXE /dev/loop0 (loopback ISO)
+# not available when booted from local disk installation
+def mount_live():
+ if os.path.ismount("/live"):
+ return
+
+ live_dev="/dev/live"
+ if not os.path.exists(live_dev):
+ ret = os.system("losetup /dev/loop0|grep -q '\.iso'")
+ if ret == 0:
+ # PXE boot
+ live_dev="/dev/loop0"
+ else:
+ return False
+ os.system("mkdir -p /live")
+ os.system("mount -r %s /live || mount %s /live") % (live_dev, live_dev)
+
+
+# mount root partition
+# boot loader + kernel + initrd + LiveOS
+def mount_liveos():
+ if os.path.ismount("/liveos"):
+ return
+ else:
+ os.system("mkdir -p /liveos")
+ os.system("mount LABEL=Root /liveos")
+
+# mount config partition
+# /config for persistance
+def mount_config():
+ # Only try to mount /config if the persistent storage exists
+ if os.path.exists("/dev/HostVG/Config"):
+ os.system("mkdir -p /config")
+ if not os.path.ismount("/config"):
+ ret = os.system("mount /dev/HostVG/Config /config")
+ if ret > 0:
+ return False
+
+ # optional config embedded in the livecd image
+ if os.path.exists("/live/config"):
+ os.system("cp -rv --update /live/config/* /config")
+
+ # bind mount all persisted configs to rootfs
+ for f in os.listdir("/config"):
+ if os.path.isfile("/config/%s") % f:
+ target = string.replace(f, "/config", "")
+ mounted = os.system("grep -q %s ext3 /proc/mounts") % target
+ if mounted == 0:
+ # skip if already bind-mounted
+ pass
+ else:
+ dirname = os.path.dirname(target)
+ os.system("mkdir -p %s") % dirname
+ if not os.path.exists(target):
+ os.open(target, 'w').close()
+ os.system("mount -n --bind %s %s" % (f,target))
+ return True
+ else:
+ # /config is not available
+ return False
+
+def mount_boot():
+ if os.path.ismount("/boot"):
+ return
+ else:
+ os.system("mkdir -p /boot")
+ os.system("mount LABEL=Boot /boot")
+
+# stop any service which keeps /var/log busy
+# keep the list of services
+def unmount_logging_services():
+ # mapping command->service is lame, but works for most initscripts
+ logging_services=""
+ prgs_cmd = "cd /etc/init.d|sudo lsof -Fc +D /var/log|grep ^c|sort -u"
+ prgs = subprocess.Popen(prgs_cmd, shell=True, stdout=PIPE, stderr=STDOUT)
+ prgs_output = prgs.stdout.read()
+ for prg in prgs_output:
+ srvs = subprocess.Popen("grep -l ${prg#c}$ *", shell=True, stdout=PIPE, stderr=STDOUT)
+ srvs_output = srvs.stdout.read()
+ for svc in srv_output:
+ os.system("service %s stop") % svc
+ logging_services+= svc
+ # debugging help
+ os.system("lsof +D /var/log")
+
+# mount logging partition
+# this only gets executed when disk is re-partitioned, HostVG/Logging is empty
+def mount_logging():
+ if os.path.ismount("/var/log"):
+ return True
+ if os.path.exists("/dev/HostVG/Logging"):
+ log("Mounting log partition")
+ # temporary mount-point
+ log2 = tempfile.mkdtemp()
+ os.system("mount /dev/HostVG/Logging %s") % log2
+ unmount_logging_services()
+ # save logs from tmpfs
+ os.system("cp -av /var/log/* %s") % log2
+ # save temporary log
+ if os.path.exists("/tmp/ovirt.log"):
+ os.system("cp /tmp/ovirt.log %s/ovirt.log-tmp") % log2
+ os.system("mount --move %s /var/log") % log2
+ shutil.rmtree(log2)
+ os.system("restorecon -rv /var/log")
+ for srv in logging_services:
+ os.system("service %s start") % srv
+ return
+ else:
+ # /var/log is not available
+ log("\nThe logging partion has not been created. Please create it at the main menu.\n")
+ return False
+
+def unmount_logging():
+ if not os.path.ismount("/var/log"):
+ return True
+ log("Unmounting log partition")
+ # plymouthd keeps /var/log/boot.log
+ ret = os.system("plymouth --ping")
+ if ret == 0:
+ os.system("plymouth --quit")
+ unmount_logging_services()
+
+ ret = os.system("umount /var/log")
+ if ret > 0:
+ return ret
+ for srv in logging_services:
+ os.system("service %s start") % srv
+ return
+
+# mount data partition
+def mount_data():
+ if os.path.ismount("/data"):
+ return
+
+ if os.path.exists("/dev/HostVG/Data"):
+ os.system("mkdir -p /data")
+ os.system("mount /data")
+ os.system("mkdir -p /data/images")
+ os.system("mkdir -p /var/lib/libvirt/images")
+ os.system("mount /var/lib/libvirt/images")
+ os.system("restorecon -rv /var/lib/libvirt/images")
+ os.system("mkdir -p /data/core")
+ os.system("mkdir -p /var/log/core")
+ os.system("mount /var/log/core")
+ os.system("restorecon -rv /var/log/core")
+ return
+ else:
+ # /data is not available
+ log("\nThe data partion has not been created. Please create it at the main menu.\n")
+ return False
+
+def md5sum(filename):
+ m = md5()
+ with open(filename) as f:
+ data = f.read(buf_size)
+ while data:
+ m.update(data)
+ data = f.read(buf_size)
+ return m.hexdigest()
+
+
+# persist configuration to /config
+# ovirt_store_config /etc/config /etc/config2 ...
+# copy to /config and bind-mount back
+
+def ovirt_store_config(files):
+ if os.path.ismount("/config"):
+ for p in files:
+ filename = os.path.abspath(filename)
+ persist_it=true
+
+ # ensure that, if this is a directory
+ # that it's not already persisted
+ if os.path.isdir(filename):
+ if os.path.isdir("/config/" + filename):
+ log("Directory already persisted: ${filename}\n")
+ log("You need to unpersist its child directories and/or files and try again.\n")
+ persist_it=false
+
+ # if it's a file then make sure it's not already
+ # persisted
+ if os.path.isfile(filename):
+ if os.path.isfile("/config/" + filename):
+ md5root=md5sum(filename)
+ md5stored=md5sum("/config" + filename)
+ if md5root == md5stored:
+ log("File already persisted: %s\n") % filename
+ persist_it=false
+ else:
+ # persistent copy needs refresh
+ ret = os.system("umount -n %s 2> /dev/null") % filename
+ if ret != 0:
+ os.system("rm -f /config%s") % filename
+
+ if persist_it:
+ # skip if file does not exist
+ if not os.path.exists(filename):
+ log("Skipping, file '%s' does not exist\n") % filename
+ # skip if already bind-mounted
+ if not check_bind_mount(filename):
+ dirname = os.path.dirname(filename)
+ os.system("mkdir -p /config/%s") % dirname
+ ret = os.system("cp -a %s /config%s") % (filename, filename)
+ if ret == 0:
+ ret = os.system("mount -n --bind /config%s %s") % (filename, filename)
+ if ret != 0:
+ log("Failed to persist\n")
+ rc=1
+ else:
+ log("File persisted\n")
+ # register in /config/files used by rc.sysinit
+ ret = os.system("grep -q \"^$%s$\" /config/files 2> /dev/null") % filename
+ if ret > 0:
+ os.system("echo \"%s\n\" >> /config/files") % filename
+ log("\nSuccessfully persisted ${filename}\n")
+ else:
+ log("WARNING: persistent config storage not available\n")
+ rc=2
+ return rc
+
+def is_persisted(filename):
+ abspath = os.path.abspath(filename)
+ if os.path.exists("/config" + abspath):
+ return True
+ else:
+ return False
+
+# unmount bindmounted config files
+# unmount_config /etc/config /etc/config2 ...
+#
+# Use before running commands which fail on bindmounted files.
+# After the file is replaced, call ovirt_store_config /etc/config ...
+# to bindmount the config file again.
+#
+
+def check_bind_mount(config_file):
+ bind_mount_cmd = "grep -q \"%s ext4\" /proc/mounts" % config_file
+ bind_mount_ret = os.system(bind_mount_cmd)
+ return bind_mount_ret
+
+def unmount_config(config_file):
+ if os.path.ismount("/config"):
+ for p in config_file.split():
+ f = os.path.abspath(p)
+ if check_bind_mount(f):
+ ret = os.system("umount -n %s" % f)
+ if ret == 0:
+ if os.path.exists("/config%s" % f):
+ # refresh the file in rootfs if it was mounted over
+ shutil.copy("/config%s %s" % (f,f))
+
+# remove persistent config files
+# remove_config /etc/config /etc/config2 ...
+#
+def remove_config(config_file):
+ # if there are no persisted files then just exit
+ if os.path.exists("/config/files"):
+ if os.path.getsize('/config/files') == 0:
+ print "There are currently no persisted files."
+ return
+ if os.path.ismount("/config"):
+ for p in config_file:
+ filename = os.path.abspath(filename)
+ ret = os.system("grep \"^%s\$\" /config/files > /dev/null 2>&1") % filename
+ if ret == 0:
+ if check_bind_mount(filename):
+ ret = os.system("umount -n %s") % filename
+ if ret == 0:
+ if os.path.isdir(filename):
+ ret = os.system("cp -ar /config/%s/* %s") % (filename,filename)
+ if ret > 0:
+ log(" Failed to unpersist %s\n") % filename
+ return False
+ else:
+ log("%s successully unpersisted\n") % filename
+ return True
+ else:
+ if os.path.isfile(filename):
+ # refresh the file in rootfs if it was mounted over
+ ret = os.system("cp -a /config%s") % (filename, filename)
+ if ret > 0:
+ log("Failed to unpersist %s\n") % filename
+ return False
+ else:
+ log("%s successully unpersisted\n") % filename
+ # clean up the persistent store
+ os.system("rm -Rf /config%s") % filename
+ # unregister in /config/files used by rc.sysinit
+ os.system("sed --copy -i \"\|^%s$|d\" /config/files") % filename
+ else:
+ log("%s is not a persisted file.\n") % filename
+ else:
+ log("File not explicitly persisted: %s\n") % filename
+
+# ovirt_safe_delete_config
+# ovirt_safe_delete_config /etc/config /etc/config2 ...
+#
+# Use to *permanently* remove persisted configuration file.
+# WARNING: file is shredded and removed
+#
+def ovirt_safe_delete_config(target):
+ for t in target:
+ if check_bind_mount(target):
+ os.system("umount -n %s") % target
+
+ os.system("sed --copy -i \"\|%s$|d\" /config/files") % target
+
+ if os.path.isdir(target):
+ child = subprocess.Popen("ls -d %s')", shell=True, stdout=PIPE, stderr=STDOUT) % target
+ child_output = swap.stdout.read()
+ for child in child_output:
+ ovirt_safe_delete_config(child)
+ os.system("rm -rf /config%s") % target
+ os.system("rm -rf %s") % target
+ else:
+ os.system("shred -u /config%s") % target
+ os.system("shred -u %s") % target
+
+
+# compat function to handle different udev versions
+def udev_info(name, query):
+ # old udev command with shortopts
+ udev_cmd = "udevinfo -n %s -q %s" % (name, query)
+ udev = subprocess.Popen(udev_cmd, shell=True, stdout=PIPE, stderr=STDOUT)
+ udev_output = udev.stdout.read()
+ udev.poll()
+ udev_rc = udev.returncode()
+ if udev_rc > 0:
+ udev_cmd = "udevadm info --name=%s --query=%s" % (name, query)
+ udev = subprocess.Popen(udev_cmd, shell=True, stdout=PIPE, stderr=STDOUT)
+ udev_output = udev.stdout.read()
+ udev.poll()
+ udev_rc = udev.returncode()
+ if udev_rc == 0:
+ os.system("echo %s") % udev_output
+ return udev_output
+
+def backup_file(file):
+ dir = os.path.dirname(file)
+ if dir in os.listdir("/"):
+ print "unexpected non-absolute dir: %s" % dir
+ sys.exit(1)
+ os.system("mkdir -p %s%s") % (OVIRT_BACKUP_DIR, dir)
+ if os.path.exists(file):
+ shutil.copy(file, OVIRT_BACKUP_DIR + file)
+
+# reboot wrapper
+# cleanup before reboot
+def reboot():
+ if not OVIRT_VARS.has_key("$OVIRT_ISCSI_ENABLED"):
+ # setup new Root if update is prepared
+ ret = os.system("findfs LABEL=RootUpdate >/dev/null 2>&1")
+ if ret == 0:
+ root_update_dev_lookup = subprocess.Popen("findfs LABEL=RootUpdate >/dev/null 2>&1", shell=True, stdout=PIPE, stderr=STDOUT)
+ root_update_dev = root_update_dev_lookup.stdout.read()
+ root_dev_lookup = subprocess.Popen("findfs LABEL=Root >/dev/null 2>&1", shell=True, stdout=PIPE, stderr=STDOUT)
+ root_dev = root_dev_lookup.stdout.read()
+ os.system("e2label %s RootBackup") % root_dev
+ os.system("e2label %s Root") % root_update_dev
+ # run post-install hooks
+ # e.g. to avoid reboot loops using Cobbler PXE only once
+ # Cobbler XMLRPC post-install trigger (XXX is there cobbler SRV record?):
+ # wget "http://192.168.50.2/cblr/svc/op/trig/mode/post/system/$(hostname)"
+ # -O /dev/null
+ for hook in os.listdir("/etc/ovirt-config-boot.d"):
+ os.system(hook)
+ os.system("/sbin/reboot")
+
+# Check if networking is already up
+def network_up():
+ ret = os.system("ip addr show | grep -q 'inet.*scope global'")
+ if ret == 0:
+ return True
+ return False
+# Cleans partition tables
+def wipe_partitions(drive):
+ log("Wiping old boot sector")
+ os.system("dd if=/dev/zero of=\"%s\" bs=1024K count=1") % drive
+ # zero out the GPT secondary header
+ log("Wiping secondary gpt header")
+ disk_kb = subprocess.Popen("sfdisk -s \"%s\" 2>/dev/null", shell=True, stdout=PIPE, stderr=STDOUT) % drive
+ disk_kb_count = disk_kb.stdout.read()
+ os.system("dd if=/dev/zero of=\"%s\" bs=1024 seek=$((%s - 1)) count=1") % (drive,disk_kb_count)
+
+def test_ntp_configuration():
+ # stop ntpd service for testing
+ os.system("service ntpd stop > /dev/null 2>&1")
+ for server in ntp_servers:
+ ret = os.system("ntpdate %s > /dev/null 2>&1") % server
+ if ret > 0:
+ print "\n Unable to verify NTP server: %s \n" % server
+ else:
+ log("\n Verified NTP server: %s \n") % server
+ os.system("service ntpd start")
+
+def get_dm_device(device):
+ dev_path = os.path.abspath(device)
+ major_cmd = "ls -al %s|awk {'print $5'}" % dev_path
+ major = subprocess.Popen("major_dev_cmd", shell=True, stdout=PIPE, stderr=STDOUT)
+ major = major.stdout.read()
+ minor_cmd = "ls -al %s|awk {'print $6'}" % dev_path
+ minor = subprocess.Popen("minor_dev_cmd", shell=True, stdout=PIPE, stderr=STDOUT)
+ minor = minor.stdout.read()
+ dm_device=""
+ rc = 1
+ for dm in os.listdir("/dev/mapper/"):
+ dm_major_cmd = "ls -al %s |awk {'print $5'}" % dm
+ dm_major = subprocess.Popen("dm_major_dev_cmd", shell=True, stdout=PIPE, stderr=STDOUT)
+ dm_major = dm_major.stdout.read()
+ dm_major = dm_major.strip(",")
+ dm_minor_cmd = "ls -al %s|awk {'print $6'}" % dm
+ dm_minor = subprocess.Popen("dm_minor_dev_cmd", shell=True, stdout=PIPE, stderr=STDOUT)
+ dm_minor = dm_minor.stdout.read()
+ if dm_major == major and dm_minor == minor:
+ dm_device=dm
+ return dm_device
+
diff --git a/scripts/storage.py b/scripts/storage.py
new file mode 100644
index 0000000..a2a83a5
--- /dev/null
+++ b/scripts/storage.py
@@ -0,0 +1,451 @@
+#!/usr/bin/python
+from ovirtfunctions import *
+import os
+import time
+import re
+import subprocess
+from subprocess import PIPE, STDOUT
+
+
+default_overcommit=0.5
+default_boot_size=50
+default_root_size=256
+default_config_size=5
+default_logging_size=2048
+BOOTDRIVE = ""
+RootBackup_end = ""
+# -1 indicates data partition should use remaining disk
+default_data_size=-1
+
+mem_size_cmd = "awk '/MemTotal:/ { print $2 }' /proc/meminfo"
+mem_size_mb = subprocess.Popen(mem_size_cmd, shell=True, stdout=PIPE, stderr=STDOUT)
+MEM_SIZE_MB = mem_size_mb.stdout.read()
+
+MEM_SIZE_MB= int(MEM_SIZE_MB) / 1024
+
+overcommit=default_overcommit
+# we multiply the overcommit coefficient by 10 then divide the
+# product by 10 to avoid decimals in the result
+OVERCOMMIT_SWAP_SIZE = int(MEM_SIZE_MB) * overcommit * 10 / 10
+
+# add to the swap the amounts from http://kbase.redhat.com/faq/docs/DOC-15252
+MEM_SIZE_GB= MEM_SIZE_MB/1024
+if MEM_SIZE_GB < 4:
+ BASE_SWAP_SIZE=2048
+elif MEM_SIZE_GB < 16:
+ BASE_SWAP_SIZE=4096
+elif MEM_SIZE_GB < 64:
+ BASE_SWAP_SIZE=8192
+else:
+ BASE_SWAP_SIZE=16384
+
+CALC_SWAP_SIZE = int(BASE_SWAP_SIZE) + int(OVERCOMMIT_SWAP_SIZE)
+
+BOOT_SIZE=default_boot_size
+SWAP_SIZE=CALC_SWAP_SIZE
+ROOT_SIZE=default_root_size
+CONFIG_SIZE=default_config_size
+LOGGING_SIZE=default_logging_size
+DATA_SIZE=default_data_size
+
+def translate_multipath_device(dev):
+ #trim so that only sdX is stored, but support passing /dev/sdX
+ if dev is None:
+ return False
+ if "/dev/mapper" in dev:
+ return dev
+ mpath_cmd = "multipath -ll %s" % dev
+ ret = os.system(mpath_cmd)
+ if ret > 0:
+ return dev
+ dm_dev_cmd = "multipath -ll \"%s\" | egrep dm-[0-9]+ | sed -r 's/^.& (dm-[0-9]+) .*$/\1/'" % dev
+ dm_dev = subprocess.Popen(dm_dev_cmd, shell=True, stdout=PIPE, stderr=STDOUT)
+ dm_dev_output = "/dev/" + dm_dev.stdout.read()
+ mpath_device = get_dm_device(dm_dev_output)
+
+ if mpath_device is None:
+ return dev
+ else:
+ return mpath_device
+
+def get_drive_size(drive):
+ size_cmd = "sfdisk -s %s 2>null" % drive
+ size = subprocess.Popen(size_cmd, shell=True, stdout=PIPE, stderr=STDOUT)
+ size = size.stdout.read()
+# size = int(size.stdout.read().strip()) # / 1024)
+ return size
+
+if OVIRT_VARS.has_key("OVIRT_INIT"):
+ # if present, use the drive selected with 'ovirt_init' boot parameter
+ # setting these the same until kernel cmdline argument implemented
+ DRIVE=translate_multipath_device(OVIRT_VARS["OVIRT_INIT"])
+ ROOTDRIVE = DRIVE
+ HOSTVGDRIVE = DRIVE
+ ROOTDRIVESPACE = get_drive_size(ROOTDRIVE)
+
+# if the node is Fedora then use GPT, otherwise use MBR
+if os.path.isfile("/etc/fedora-release"):
+ LABEL_TYPE="gpt"
+else:
+ LABEL_TYPE="msdos"
+
+
+
+##################################################################
+
+def wipe_lvm_on_disk(dev):
+ unmount_logging
+ part_delim="p"
+ if "/dev/sd" in dev:
+ part_delim=""
+ vg_cmd = "pvs -o vg_uuid --noheadings %s \"%s%s[0-9]\"* 2>/dev/null|sort -u" % (dev, dev, part_delim)
+ vg = subprocess.Popen(vg_cmd, shell=True, stdout=PIPE, stderr=STDOUT)
+ vg_output = vg.stdout.read()
+ for vg in vg_output:
+ pvs = os.system("pvscan -o pv_name,vg_uuid --noheadings | grep \"%s\" | egrep -v -q \"%s%s[0-9]+|%s \" 2>/dev/null") % (vg, dev, part_delim, dev)
+ if pvs > 0:
+ log("The volume group \"%s\" spans multiple disks.") % vg
+ log("This operation cannot complete. Please manually")
+ log("cleanup the storage using standard disk tools.")
+ sys.exit(1)
+ wipe_volume_group(vg)
+ return
+
+
+def reread_partitions(drive):
+ if "dev/mapper" in drive:
+ # kpartx -a -p p "$drive"
+ # XXX fails with spaces in device names (TBI)
+ # ioctl(3, DM_TABLE_LOAD, 0x966980) = -1 EINVAL (Invalid argument)
+ # create/reload failed on 0QEMU QEMU HARDDISK drive-scsi0-0-0p1
+ os.system("partprobe ||:")
+ # partprobe fails on cdrom:
+ # Error: Invalid partition table - recursive partition on /dev/sr0.
+ else:
+ os.system("blockdev --rereadpt %s") % drive
+
+
+def get_sd_name(id):
+ device_sys_cmd = "grep -H \"^%s$\" /sys/block/*/dev | cut -d: -f1" % id
+ device_sys = subprocess.Popen(device_sys_cmd, shell=True, stdout=PIPE, stderr=STDOUT)
+ device_sys_output = device_sys.stdout.read().strip()
+ if not device_sys_output is "":
+ device = os.path.basename(device_sys_output)
+ return device
+ return False
+
+
+# gets the dependent block devices for multipath devices
+def get_multipath_deps(mpath_device, deplist_var):
+ return
+ deplist=""
+ #get dependencies for multipath device
+ deps_cmd ="dmsetup deps -u \"mpath-%s\" \
+ | sed -r 's/\(([0-9]+), ([0-9]+)\)/\1:\2/g' \
+ | sed 's/ /\n/g' | grep [0-9]:[0-9] 2>/dev/null" % mpath_device
+ deps = subprocess.Popen(deps_cmd, shell=True, stdout=PIPE, stderr=STDOUT)
+ deps_output = deps.stdout.read()
+
+ for dep in deps_output.split():
+ device=get_sd_name(dep)
+ dep_list=[]
+ if device is None:
+ if deplist is None:
+ deplist = device
+ else:
+ deplist.append(device)
+ return deplist
+
+# Find a usable/selected storage device.
+# If there are none, give a diagnostic and return nonzero.
+# If there is just one, e.g., /dev/sda, treat it as selected (see below).
+# and return 0. If there are two or more, make the user select one
+# or decline. Upon decline, return nonzero. Otherwise, print the
+# selected name, then return 0.
+# Sample output: /dev/sda
+def get_dev_name():
+ devices=""
+ # list separator
+ for d in os.listdir("/sys/block/"):
+ if re.match("^[hsv]+d", d):
+ devices="/dev/%s %s" % (d,devices)
+ byid_list_cmd = "find /dev/disk/by-id -mindepth 1 -not -name '*-part*' 2>/dev/null"
+ byid_list = subprocess.Popen(byid_list_cmd, shell=True, stdout=PIPE, stderr=STDOUT)
+ byid_list_output = byid_list.stdout.read()
+ for d in byid_list_output.split():
+ d = os.readlink(d)
+ d_basename = os.path.basename(d)
+ udev_cmd = "udevadm info --name=/dev/%s --query=property | grep -q ^ID_BUS:" % d_basename
+ if os.system(udev_cmd):
+ devices="/dev/%s %s" % (d_basename, devices)
+ # FIXME: workaround for detecting cciss devices
+ if os.path.exists("/dev/cciss"):
+ for d in os.listdir("/dev/cciss"):
+ if not re.match("p[0-9]+\$", d):
+ devices="%s %s" % (d, devices)
+
+ # include multipath devices
+ devs_to_remove=""
+ multipath_list_cmd = "dmsetup ls --target=multipath | cut -f1"
+ multipath_list = subprocess.Popen(multipath_list_cmd, shell=True, stdout=PIPE, stderr=STDOUT)
+ multipath_list_output = multipath_list.stdout.read()
+
+ for d in multipath_list_output:
+ devices="/dev/mapper/%s %s" % (d, devices)
+ sd_devs=""
+ get_multipath_deps(d, sd_devs)
+
+ dm_dev_cmd = "multipath -ll \"%s\" | grep \"%s\" | sed -r 's/^.*(dm-[0-9]+ ).*$/\1/' )" % (d, d)
+ dm_dev = subprocess.Popen(dm_dev_cmd, shell=True, stdout=PIPE, stderr=STDOUT)
+ dm_dev_output = dm_dev.stdout.read()
+ devs_to_remove="%s %s %s" % (devs_to_remove, sd_devs, dm_dev)
+ # Remove /dev/sd* devices that are part of a multipath device
+ dev_list=[]
+ for d in devices.split():
+ if os.path.basename(d) not in devs_to_remove:
+ dev_list.append(d)
+
+ for dev in dev_list:
+ if dev_list.count(dev) > 1:
+ count = dev_list.count(dev)
+ while (count > 1):
+ dev_list.remove(dev)
+ count = count - 1
+
+ return dev_list
+
+def check_partition_sizes():
+ # disk_size need_size
+ drive_list = []
+ drive_space_dict = {}
+ min_data_size = OVIRT_VARS["DATA_SIZE"]
+ if DATA_SIZE == -1 :
+ min_data_size=5
+
+ if OVIRT_VARS["OVIRT_ISCSI_ENABLED"] == "y":
+ BOOTDRIVESPACE = get_drive_size(BOOTDRIVE)
+ drive_list.append("BOOT")
+ drive_space_dict["BOOTDRIVESPACE"] = BOOTDRIVESPACE
+ drive_space_dict["BOOT_NEED_SIZE"] = BOOT_SIZE
+ else:
+ ROOTDRIVESPACE = get_drive_size(ROOTDRIVE)
+ HOSTVGDRIVESPACE = get_drive_size(HOSTVGDRIVE)
+ ROOT_NEED_SIZE=ROOT_SIZE * 2
+ HOSTVG_NEED_SIZE=SWAP_SIZE + CONFIG_SIZE + LOGGING_SIZE + min_data_size
+ drive_space_dict["ROOTDRIVESPACE"] = ROOTDRIVESPACE
+ drive_space_dict["ROOT_NEED_SIZE"] = ROOT_NEED_SIZE
+ drive_space_dict["HOSTVGDRIVESPACE"] = HOSTVGDRIVESPACE
+ drive_space_dict["HOSTVG_NEED_SIZE"] = HOSTVG_NEED_SIZE
+
+ if ROOTDRIVE == HOSTVGDRIVE:
+ drive_list.append("ROOT")
+ ROOT_NEED_SIZE=ROOT_SIZE * 2 + HOSTVG_NEED_SIZE
+ drive_space_dict["ROOT_NEED_SIZE"] = ROOT_NEED_SIZE
+ else:
+ drive_list.append("ROOT")
+ drive_list.append("HOSTVG")
+
+ for drive in drive_list:
+ drive_need_size = drive_space_dict["drive" + "NEED_SIZE"]
+ drive_disk_size= drive_space_dict["drive" + "DRIVESPACE"]
+
+ if drive_need_size > drive_disk_size:
+ gap_size = drive_need_size - drive_disk_size
+ log("\n")
+ log("=============================================================\n")
+ log("The target storage device is too small for the desired sizes:\n")
+ log(" Disk Target: $drive \n")
+ log(" Size of target storage device: $drive_disk_size MB\n")
+ log(" Total storage size to be used: $drive_need_size MB\n")
+ log("\n")
+ log("You need an additional $gap_size MB of storage.\n")
+ log("\n")
+ sys.exit(1)
+ else:
+ log("Required Space : $drive_need_size MB\n\n")
+
+
+#def get_drive_size(drive):
+# size_cmd = "sfdisk -s \"%s\" 2>null)" % drive
+# size = subprocess.Popen(size_cmd, shell=True, stdout=PIPE, stderr=STDOUT)
+# size_output = size.stdout.read()
+# size_output = size_output / 1024
+# log("%s (%s MB)") % (drive, size_output)
+# if not space_var is None:
+# return space_var
+
+def check_existing_hostvg(install_dev):
+ if install_dev is None:
+ devices="$(pvs --separator=\" \" -o pv_name,vg_name --noheadings | grep \"HostVG\" | cut -f1)"
+ else:
+ devices="$(pvs --separator=\" \" -o pv_name,vg_name --noheadings | grep -v \"%s\" | grep \"HostVG\" | cut -f1)" % install_dev
+ if devices is not None:
+ log("\n")
+ log("There appears to already be an installation on another device:\n")
+ for device in devices.split():
+ log("\t%s\n") % device
+ log("The installation cannot proceed until the device is removed\n")
+ log("from the system of the HostVG volume group is removed.\n")
+ return devices
+
+#ROOTDRIVE = "/dev/sda"
+def create_hostvg():
+ BOOTDRIVE = ""
+ log("Creating LVM partition")
+ global ROOTDRIVE
+ global HOSTVGDRIVE
+ global BOOTDRIVE
+ global RootBackup_end
+ if ROOTDRIVE == HOSTVGDRIVE:
+ parted_cmd = "parted %s -s \"mkpart primary ext2 %sM -1\"" % (HOSTVGDRIVE, RootBackup_end)
+ os.system(parted_cmd)
+ hostvgpart="3"
+ elif BOOTDRIVE == HOSTVGDRIVE:
+ parted_cmd = "parted %s -s \"mkpart primary ext2 %s -1\"" % (HOSTVGDRIVE, boot_size_si)
+ os.system(parted_cmd)
+ hostvgpart="2"
+ ROOTDRIVE = BOOTDRIVE
+ else:
+ os.system("parted \"%s\" -s \"mkpart primary ext2 0M -1") % HOSTVGDRIVE
+ hostvgpart = "1"
+ log("Toggling LVM on")
+ parted_cmd = "parted \"%s\" -s \"set %s lvm on\"" % (HOSTVGDRIVE, hostvgpart)
+ os.system(parted_cmd)
+ os.system("parted \"%s\" -s \"print\"") % ROOTDRIVE
+ os.system("udevadm settle 2> /dev/null || udevsettle")
+ reread_partitions(HOSTVGDRIVE)
+
+ # sync GPT to the legacy MBR partitions
+ if OVIRT_VARS.has_key["OVIRT_INSTALL_ROOT"] and OVIRT_VARS["OVIRT_INSTALL_ROOT"] == "y" :
+ if self.LABEL_TYPE == "gpt":
+ log("Running gptsync to create legacy mbr")
+ os.system("gptsync \"%s\"") % ROOTDRIVE
+
+ partpv = HOSTVGDRIVE + hostvgpart
+ if not os.path.exists(partpv):
+ # e.g. /dev/cciss/c0d0p2
+ partpv = HOSTVGDRIVE + "p" + hostvgpart
+ log("Creating physical volume")
+ if not os.path.exists(partpv):
+ log("%s is not available!") % partpv
+ sys.exit(1)
+ os.system("dd if=/dev/zero of=\"%s\" bs=1024k count=1") % partpv
+ os.system("pvcreate -ff -y \"%s\"") % partpv
+ log("Creating volume group")
+ os.system("vgcreate /dev/HostVG \"%s\"") % partpv
+
+ if SWAP_SIZE > 0:
+ log("Creating swap partition")
+ os.system("lvcreate --name Swap --size %sM /dev/HostVG") % SWAP_SIZE
+ os.system("mkswap -L \"SWAP\" /dev/HostVG/Swap")
+ os.system("echo \"/dev/HostVG/Swap swap swap defaults 0 0\" >> /etc/fstab")
+ if CONFIG_SIZE > 0:
+ log("Creating config partition")
+ os.system("lvcreate --name Config --size %sM /dev/HostVG") % CONFIG_SIZE
+ os.system("mke2fs -j /dev/HostVG/Config -L \"CONFIG\"")
+ os.system("tune2fs -c 0 -i 0 /dev/HostVG/Config")
+ if LOGGING_SIZE > 0:
+ log("Creating log partition")
+ os.system("lvcreate --name Logging --size %sM /dev/HostVG") % LOGGING_SIZE
+ os.system("mke2fs -j /dev/HostVG/Logging -L \"LOGGING\"")
+ os.system("tune2fs -c 0 -i 0 /dev/HostVG/Logging")
+ os.system("echo \"/dev/HostVG/Logging /var/log ext3 defaults,noatime 0 0\" >> /etc/fstab")
+ use_data=1
+ if DATA_SIZE == -1:
+ log("Creating data partition with remaining free space")
+ os.system("lvcreate --name Data -l 100%FREE /dev/HostVG")
+ use_data=0
+ elif DATA_SIZE > 0:
+ log("Creating data partition")
+ os.system("lvcreate --name Data --size %sM /dev/HostVG") % DATA_SIZE
+ use_data=0
+
+ if use_data == 0:
+ os.system("mke2fs -j /dev/HostVG/Data -L \"DATA\"")
+ os.system("tune2fs -c 0 -i 0 /dev/HostVG/Data")
+ os.system("echo \"/dev/HostVG/Data /data ext3 defaults,noatime 0 0\" >> /etc/fstab")
+ os.system("echo \"/data/images /var/lib/libvirt/images bind bind 0 0\" >> /etc/fstab")
+ os.system("echo \"/data/core /var/log/core bind bind 0 0\" >> /etc/fstab")
+
+ log("Mounting config partition")
+ if mount_config:
+ ovirt_store_config /etc/fstab
+
+ mount_logging
+ if use_data == 0:
+ log("Mounting data partition")
+ mount_data
+ log("Completed!")
+
+
+def perform_partitioning():
+ if HOSTVGDRIVE is None and OVIRT_VARS["OVIRT_ISCSI_ENABLED"] != "y":
+ log("\nNo storage device selected.\n")
+ return False
+
+ if BOOTDRIVE is None and OVIRT_VARS["OVIRT_ISCSI_ENABLED"] == "y":
+ log("\nNo storage device selected.\n")
+ return False
+
+ log("Saving parameters")
+ unmount_config("/etc/default/ovirt")
+
+ log("Removing old LVM partitions")
+ wipe_volume_group("HostVG")
+ wipe_lvm_on_disk(HOSTVGDRIVE)
+ wipe_lvm_on_disk(ROOTDRIVE)
+
+ mem_size_cmd = "awk '/MemTotal:/ { print $2 }' /proc/meminfo"
+ mem_size_mb = subprocess.Popen(mem_size_cmd, shell=True, stdout=PIPE, stderr=STDOUT)
+ MEM_SIZE_MB = int(mem_size_mb.stdout.read())
+
+ MEM_SIZE_MB = MEM_SIZE_MB / 1024
+ boot_size_si = BOOT_SIZE * (1024 * 1024) / (1000 * 1000)
+
+ if OVIRT_VARS.has_key("OVIRT_ISCSI_ENABLED") and OVIRT_VARS["OVIRT_ISCSI_ENABLED"] == "y":
+ log("iSCSI enabled, partitioning boot drive: $BOOTDRIVE")
+ wipe_partitions(BOOTDRIVE)
+ reread_partitions(BOOTDRIVE)
+ log("Creating boot partition")
+ os.system("parted \"%s\" -s \"mklabel %s\"") % (BOOTDRIVE, LABEL_TYPE)
+ os.system("parted \"%s\" -s \"mkpartfs primary ext2 0M %sM") % (BOOTDRIVE, boot_size_si)
+ reread_partitions(BOOTDRIVE)
+ partboot= BOOTDRIVE + "1"
+ if not os.path.exists(partboot):
+ partboot = BOOTDRIVE + "p1"
+ # sleep to ensure filesystems are created before continuing
+ time.sleep("10")
+ os.system("mke2fs \"%s\" -L Boot") % partboot
+ os.system("tune2fs -c 0 -i 0 %s") % partboot
+ if OVIRT_VARS["OVIRT_ISCSI_HOSTVG"] == "y":
+ create_hostvg()
+ log("Completed!")
+ return
+
+ if OVIRT_VARS.has_key("OVIRT_ROOT_INSTALL") and OVIRT_VARS["OVIRT_ROOT_INSTALL"] == "y":
+ log("Partitioning root drive: %s") % ROOTDRIVE
+ wipe_partitions(ROOTDRIVE)
+ reread_partitions(ROOTDRIVE)
+ log("Labeling Drive: %s") % ROOTDRIVE
+ os.system("parted \"%s\" -s \"mklabel %s\"") % (ROOTDRIVE, LABEL_TYPE)
+ log("Creating Root and RootBackup Partitions")
+ RootBackup_end= ROOT_SIZE * 2
+ os.system("parted \"%s\" -s \"mkpart primary ext2 0M %sM\"") % (ROOTDRIVE, ROOT_SIZE)
+ os.system("parted \"%s\" -s \"mkpart primary ext2 %sM %sM\"") % (ROOTDRIVE, ROOT_SIZE, RootBackup_end)
+ # sleep to ensure filesystems are created before continuing
+ time.sleep("10")
+ reread_partitions(ROOTDRIVE)
+ partroot = ROOTDRIVE + "1"
+ partrootbackup = ROOTDRIVE + "2"
+ if not os.path.exists(partroot):
+ partroot = ROOTDRIVE + "p1"
+ partrootbackup= ROOTDRIVE + "p2"
+ os.system("mke2fs \"%s\" -L Root") % partroot
+ os.system("mke2fs \"%s\" -L RootBackup") % partrootbackup
+ os.system("tune2fs -c 0 -i 0 \"%s\"") % partroot
+ os.system("tune2fs -c 0 -i 0 \"%s\"") % partrootbackup
+
+ if ROOTDRIVE != HOSTVGDRIVE:
+ log("Labeling Drive: %s") % abc #HOSTVGDRIVE
+ os.system("parted \"%s\" -s \"mklabel %s\"") % (HOSTVGDRIVE, LABEL_TYPE)
+ create_hostvg()
--
1.7.2.3
More information about the ovirt-devel
mailing list