Commit 19a61219 authored by Alain Takoudjou's avatar Alain Takoudjou

kvm: add support for RAM and CPU hotplug

Allow to Add CPU or RAM in hotplug mode, without need of restart qemu process
parent 8dbf6b79
...@@ -41,6 +41,7 @@ parts = ${:common-parts} ...@@ -41,6 +41,7 @@ parts = ${:common-parts}
[eggs] [eggs]
recipe = zc.recipe.egg recipe = zc.recipe.egg
interpreter = python.eggs
eggs = eggs =
${python-cffi:egg} ${python-cffi:egg}
${python-cryptography:egg} ${python-cryptography:egg}
...@@ -89,7 +90,7 @@ command = ...@@ -89,7 +90,7 @@ command =
[template] [template]
recipe = slapos.recipe.template recipe = slapos.recipe.template
url = ${:_profile_base_location_}/instance.cfg.in url = ${:_profile_base_location_}/instance.cfg.in
md5sum = bf5ef731c0d8da0267a4939882b4eeee md5sum = 5a17fc127190bbc19361c5ffb10711b3
output = ${buildout:directory}/template.cfg output = ${buildout:directory}/template.cfg
mode = 0644 mode = 0644
...@@ -98,7 +99,7 @@ recipe = hexagonit.recipe.download ...@@ -98,7 +99,7 @@ recipe = hexagonit.recipe.download
ignore-existing = true ignore-existing = true
url = ${:_profile_base_location_}/instance-kvm.cfg.jinja2 url = ${:_profile_base_location_}/instance-kvm.cfg.jinja2
mode = 644 mode = 644
md5sum = e2b8f86bdc12c86e7d959b55c6d54f6d md5sum = 152b052f4b418ab3138a3e109a3ec01e
download-only = true download-only = true
on-update = true on-update = true
...@@ -107,7 +108,7 @@ recipe = hexagonit.recipe.download ...@@ -107,7 +108,7 @@ recipe = hexagonit.recipe.download
ignore-existing = true ignore-existing = true
url = ${:_profile_base_location_}/instance-kvm-cluster.cfg.jinja2.in url = ${:_profile_base_location_}/instance-kvm-cluster.cfg.jinja2.in
mode = 644 mode = 644
md5sum = 05b6004e8c7a94de14f247affcef4971 md5sum = b3ff4f80e4d607e2a977349416c66e39
download-only = true download-only = true
on-update = true on-update = true
...@@ -185,7 +186,7 @@ ignore-existing = true ...@@ -185,7 +186,7 @@ ignore-existing = true
url = ${:_profile_base_location_}/template/template-kvm-run.in url = ${:_profile_base_location_}/template/template-kvm-run.in
mode = 644 mode = 644
filename = template-kvm-run.in filename = template-kvm-run.in
md5sum = 178a24cdad77cb6c2e519ac629dd0e74 md5sum = 7f34fbb05f0cfdb6753e008e47cacf30
download-only = true download-only = true
on-update = true on-update = true
...@@ -195,7 +196,7 @@ ignore-existing = true ...@@ -195,7 +196,7 @@ ignore-existing = true
url = ${:_profile_base_location_}/template/kvm-controller-run.in url = ${:_profile_base_location_}/template/kvm-controller-run.in
mode = 644 mode = 644
filename = kvm-controller-run.in filename = kvm-controller-run.in
md5sum = 71afd2d13f6e56993ae413a168e012d7 md5sum = 9ba27ec934e0e86ef88493356a40bed5
download-only = true download-only = true
on-update = true on-update = true
...@@ -225,7 +226,7 @@ ignore-existing = true ...@@ -225,7 +226,7 @@ ignore-existing = true
url = ${:_profile_base_location_}/template/qemu-is-ready.in url = ${:_profile_base_location_}/template/qemu-is-ready.in
mode = 644 mode = 644
filename = qemu-is-ready.in filename = qemu-is-ready.in
md5sum = 0066fa0f5f3dd47bded5e5924df2550d md5sum = b304eec8e2cb71f10ea83cac22f6db12
download-only = true download-only = true
on-update = true on-update = true
......
...@@ -197,6 +197,29 @@ ...@@ -197,6 +197,29 @@
"minimum": 128, "minimum": 128,
"multipleOf": 128 "multipleOf": 128
}, },
"hotplug-slot-size": {
"title": "Size of Hotpluggable RAM slot, in MB",
"description": "Define the RAM size to plug on one hotpluggable slot in MB, understand the size of one RAM bar. The RAM hotplugged on each slot will always have the same RAM size.",
"type": "integer",
"default": 512,
"minimum": 128,
"multipleOf": 128
},
"max-ram-hotplug-size": {
"title": "Maximum hotpluggable RAM size, in MB",
"description": "Define the maximum size of hotpluggable memory. The size is in MB and should be a multiple of the defined hotpluggable slot size.",
"type": "integer",
"default": 0,
"minimum": 128,
"multipleOf": 128
},
"hotplugged-slot-amount": {
"title": "Hotplugged RAM slot amount",
"description": "Define the number of Memory slot to hotplug. Ex: if slot-hotplug-size=512 MB, then to hotplug 1G of RAM 2 slots will be used.",
"type": "integer",
"default": 0,
"minimum": 0
},
"auto-ballooning": { "auto-ballooning": {
"title": "Enable qemu auto ballooning.", "title": "Enable qemu auto ballooning.",
"description": "Enable virtio balloon device to allows KVM guests to reduce/re-increase their memory size.", "description": "Enable virtio balloon device to allows KVM guests to reduce/re-increase their memory size.",
...@@ -245,10 +268,17 @@ ...@@ -245,10 +268,17 @@
"type": "integer", "type": "integer",
"minimum": 1 "minimum": 1
}, },
"cpu-options": { "max-cpu-hotplug-count": {
"title": "CPU Additional options: cores, threads, sockets, maxcpus.", "title": "Maximum hotpluggable CPU amount",
"description": "Additional options to use with cpu-count. Options are separated by coma: [cores=cores][,threads=threads][,sockets=sockets][,maxcpus=maxcpus]. Set this option if you know what you're doing.", "description": "Specifies the maximum number of hotpluggable CPUs.",
"type": "string" "type": "integer",
"default": 1
},
"cpu-hotplug-amount": {
"title": "hotpluggable CPU amount",
"description": "Specifies the number of CPUs to hotplug",
"type": "integer",
"default": 0
}, },
"numa": { "numa": {
"title": "Simulate a multi node NUMA system.", "title": "Simulate a multi node NUMA system.",
......
...@@ -51,11 +51,15 @@ config-authorized-key = {{ dumps(slapparameter_dict.get('authorized-keys') | joi ...@@ -51,11 +51,15 @@ config-authorized-key = {{ dumps(slapparameter_dict.get('authorized-keys') | joi
config-nbd-port = {{ dumps(kvm_parameter_dict.get('nbd-port', 1024)) }} config-nbd-port = {{ dumps(kvm_parameter_dict.get('nbd-port', 1024)) }}
config-nbd2-port = {{ dumps(kvm_parameter_dict.get('nbd-port2', 1024)) }} config-nbd2-port = {{ dumps(kvm_parameter_dict.get('nbd-port2', 1024)) }}
config-ram-size = {{ dumps(kvm_parameter_dict.get('ram-size', 1024)) }} config-ram-size = {{ dumps(kvm_parameter_dict.get('ram-size', 1024)) }}
config-max-ram-hotplug-size = {{ dumps(kvm_parameter_dict.get('max-ram-hotplug-size', 0)) }}
config-hotplug-slot-size = {{ dumps(kvm_parameter_dict.get('hotplug-slot-size', 512)) }}
config-hotplugged-slot-amount = {{ dumps(kvm_parameter_dict.get('hotplugged-slot-amount', 0)) }}
config-disk-size = {{ dumps(kvm_parameter_dict.get('disk-size', 10)) }} config-disk-size = {{ dumps(kvm_parameter_dict.get('disk-size', 10)) }}
config-disk-type = {{ dumps(kvm_parameter_dict.get('disk-type', 'virtio')) }} config-disk-type = {{ dumps(kvm_parameter_dict.get('disk-type', 'virtio')) }}
config-cpu-count = {{ dumps(kvm_parameter_dict.get('cpu-count', 1)) }} config-cpu-count = {{ dumps(kvm_parameter_dict.get('cpu-count', 1)) }}
config-max-cpu-hotplug-count = {{ dumps(kvm_parameter_dict.get('max-cpu-hotplug-count', 1)) }}
config-cpu-hotplug-amount = {{ dumps(kvm_parameter_dict.get('cpu-hotplug-amount', 0)) }}
{{ setconfig('numa', kvm_parameter_dict.get('numa', '')) }} {{ setconfig('numa', kvm_parameter_dict.get('numa', '')) }}
{{ setconfig('machine-options', kvm_parameter_dict.get('machine-options', '')) }}
{{ setconfig('cpu-options', kvm_parameter_dict.get('cpu-options', '')) }} {{ setconfig('cpu-options', kvm_parameter_dict.get('cpu-options', '')) }}
{{ setconfig('nbd-host', kvm_parameter_dict.get('nbd-host', '')) }} {{ setconfig('nbd-host', kvm_parameter_dict.get('nbd-host', '')) }}
{{ setconfig('host2', kvm_parameter_dict.get('host2', '')) }} {{ setconfig('host2', kvm_parameter_dict.get('host2', '')) }}
......
...@@ -12,6 +12,29 @@ ...@@ -12,6 +12,29 @@
"minimum": 128, "minimum": 128,
"multipleOf": 128 "multipleOf": 128
}, },
"hotplug-slot-size": {
"title": "Size of Hotpluggable RAM slot, in MB",
"description": "Define the RAM size to plug on one hotpluggable slot in MB, understand the size of one RAM bar. The RAM hotplugged on each slot will always have the same RAM size.",
"type": "integer",
"default": 512,
"minimum": 128,
"multipleOf": 128
},
"max-ram-hotplug-size": {
"title": "Maximum hotpluggable RAM size, in MB",
"description": "Define the maximum size of hotpluggable memory. The size is in MB and should be a multiple of the defined hotpluggable slot size.",
"type": "integer",
"default": 0,
"minimum": 128,
"multipleOf": 128
},
"hotplugged-slot-amount": {
"title": "Hotplugged RAM slot amount",
"description": "Define the number of Memory slot to hotplug. Ex: if slot-hotplug-size=512 MB, then to hotplug 1G of RAM 2 slots will be used.",
"type": "integer",
"default": 0,
"minimum": 0
},
"auto-ballooning": { "auto-ballooning": {
"title": "Enable qemu auto ballooning.", "title": "Enable qemu auto ballooning.",
"description": "Enable virtio balloon device to allows KVM guests to reduce/re-increase their memory size.", "description": "Enable virtio balloon device to allows KVM guests to reduce/re-increase their memory size.",
...@@ -54,10 +77,17 @@ ...@@ -54,10 +77,17 @@
"type": "integer", "type": "integer",
"minimum": 1 "minimum": 1
}, },
"cpu-options": { "max-cpu-hotplug-count": {
"title": "CPU Additional options: cores, threads, sockets, maxcpus.", "title": "Maximum hotpluggable CPU amount",
"description": "Additional options to use with cpu-count. Options are separated by coma: [cores=cores][,threads=threads][,sockets=sockets][,maxcpus=maxcpus]. Set this option if you know what you're doing.", "description": "Specifies the maximum number of hotpluggable CPUs.",
"type": "string" "type": "integer",
"default": 1
},
"cpu-hotplug-amount": {
"title": "CPU hotplug amount",
"description": "Specifies the number of CPUs to hotplug",
"type": "integer",
"default": 0
}, },
"numa": { "numa": {
"title": "Simulate a multi node NUMA system.", "title": "Simulate a multi node NUMA system.",
......
...@@ -62,10 +62,14 @@ storage-path = ${directory:srv}/passwd ...@@ -62,10 +62,14 @@ storage-path = ${directory:srv}/passwd
bytes = 8 bytes = 8
[kvm-controller-parameter-dict] [kvm-controller-parameter-dict]
python-path = {{ python_executable }} python-path = {{ python_eggs_executable }}
vnc-passwd = ${gen-passwd:passwd} vnc-passwd = ${gen-passwd:passwd}
socket-path = ${directory:var}/qmp_socket socket-path = ${directory:var}/qmp_socket
kvm-status-path = ${directory:var}/qemu-vm-is-ready kvm-status-path = ${directory:var}/qemu-vm-is-ready
cpu-hotplug-amount = ${slap-parameter:cpu-hotplug-amount}
hotplugged-slot-amount = ${slap-parameter:hotplugged-slot-amount}
hotplug-slot-size = ${slap-parameter:hotplug-slot-size}
max-ram-hotplug-size = ${slap-parameter:max-ram-hotplug-size}
[kvm-parameter-dict] [kvm-parameter-dict]
python-path = {{ python_executable }} python-path = {{ python_executable }}
...@@ -91,9 +95,10 @@ pid-file-path = ${directory:run}/pid_file ...@@ -91,9 +95,10 @@ pid-file-path = ${directory:run}/pid_file
socket-path = ${kvm-controller-parameter-dict:socket-path} socket-path = ${kvm-controller-parameter-dict:socket-path}
smp-count = ${slap-parameter:cpu-count} smp-count = ${slap-parameter:cpu-count}
smp-options = ${slap-parameter:cpu-options} max-smp-hotplug-count = ${slap-parameter:max-cpu-hotplug-count}
ram-size = ${slap-parameter:ram-size} ram-size = ${slap-parameter:ram-size}
numa = ${slap-parameter:numa} max-ram-hotplug-size = ${slap-parameter:max-ram-hotplug-size}
hotplug-slot-size = ${slap-parameter:hotplug-slot-size}
mac-address = ${create-mac:mac-address} mac-address = ${create-mac:mac-address}
tap-mac-address = ${create-tap-mac:mac-address} tap-mac-address = ${create-tap-mac:mac-address}
...@@ -143,7 +148,7 @@ disk-cache = ${slap-parameter:disk-cache} ...@@ -143,7 +148,7 @@ disk-cache = ${slap-parameter:disk-cache}
disk-aio = ${slap-parameter:disk-aio} disk-aio = ${slap-parameter:disk-aio}
auto-ballooning = ${slap-parameter:auto-ballooning} auto-ballooning = ${slap-parameter:auto-ballooning}
machine-options = ${slap-parameter:machine-options} machine-options = ${slap-parameter:machine-options}
cpu-options = ${slap-parameter:cpu-model} cpu-hotplug-slot-size = ${slap-parameter:cpu-model}
log-file = ${directory:log}/qemu.log log-file = ${directory:log}/qemu.log
...@@ -551,14 +556,15 @@ nbd2-port = 1024 ...@@ -551,14 +556,15 @@ nbd2-port = 1024
nbd2-host = nbd2-host =
ram-size = 1024 ram-size = 1024
hotplug-slot-size = 512
max-ram-hotplug-size = 0
hotplugged-slot-amount = 0
disk-size = 10 disk-size = 10
disk-type = virtio disk-type = virtio
cpu-count = 1 cpu-count = 1
# cpu-option is a string: [cores=cores][,threads=threads][,sockets=sockets][,maxcpus=maxcpus] max-cpu-hotplug-count = 1
cpu-options = cpu-hotplug-amount = 0
# list of numa options separate by space: node,nodeid=1,cpus=9-15 node,nodeid=2,cpus=1,3,7
numa =
disk-cache = writeback disk-cache = writeback
disk-aio = native disk-aio = native
auto-ballooning = True auto-ballooning = True
......
...@@ -93,6 +93,7 @@ context = ...@@ -93,6 +93,7 @@ context =
raw netcat_bin ${netcat:location}/bin/netcat raw netcat_bin ${netcat:location}/bin/netcat
raw openssl_executable_location ${openssl:location}/bin/openssl raw openssl_executable_location ${openssl:location}/bin/openssl
raw python_executable ${buildout:executable} raw python_executable ${buildout:executable}
raw python_eggs_executable ${buildout:bin-directory}/${eggs:interpreter}
raw qemu_executable_location ${kvm:location}/bin/qemu-system-x86_64 raw qemu_executable_location ${kvm:location}/bin/qemu-system-x86_64
raw qemu_img_executable_location ${kvm:location}/bin/qemu-img raw qemu_img_executable_location ${kvm:location}/bin/qemu-img
raw qemu_start_promise_tpl ${template-qemu-ready:location}/${template-qemu-ready:filename} raw qemu_start_promise_tpl ${template-qemu-ready:location}/${template-qemu-ready:filename}
......
...@@ -45,4 +45,4 @@ paramiko = 2.0.2 ...@@ -45,4 +45,4 @@ paramiko = 2.0.2
# Required by: # Required by:
# slapos.toolbox==0.71 # slapos.toolbox==0.71
passlib = 1.6.5 passlib = 1.6.5
\ No newline at end of file
...@@ -6,40 +6,47 @@ ...@@ -6,40 +6,47 @@
import socket import socket
import time import time
import os import os
from slapos.qemuqmpclient import QemuQMPWrapper
# XXX: to be factored with slapos.toolbox qemu qmp wrapper. # XXX: to be factored with slapos.toolbox qemu qmp wrapper.
socket_path = '{{ parameter_dict.get("socket-path") }}' socket_path = '{{ parameter_dict.get("socket-path") }}'
vnc_password = '{{ parameter_dict.get("vnc-passwd") }}' vnc_password = '{{ parameter_dict.get("vnc-passwd") }}'
status_path = '{{ parameter_dict.get("kvm-status-path") }}' status_path = '{{ parameter_dict.get("kvm-status-path") }}'
cpu_hotplug_amount = {{ parameter_dict.get("cpu-hotplug-amount", 0) }}
cpu_model = '{{ parameter_dict.get("cpu-model", '') }}'
max_ram_hotplug_size = {{ parameter_dict.get("max-ram-hotplug-size", 0) }}
ram_hotplug_amount = {{ parameter_dict.get("hotplugged-slot-amount", 0) }}
slot_hotplug_size = {{ parameter_dict.get("hotplug-slot-size", 512) }}
if os.path.exists(status_path): def update():
os.unlink(status_path) if os.path.exists(status_path):
os.unlink(status_path)
# Connect to KVM qmp socket qemu_wrapper = QemuQMPWrapper(socket_path)
so = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
connected = False
while not connected:
try: try:
so.connect(socket_path) qemu_wrapper.setVNCPassword(vnc_password)
except socket.error: cpu_option_dict = {
time.sleep(1) 'device': 'cpu',
else: 'amount': cpu_hotplug_amount
connected = True }
data = so.recv(1024) if cpu_model:
cpu_option_dict['model'] = cpu_model
# Enable qmp qemu_wrapper.updateDevice(cpu_option_dict)
so.send('{ "execute": "qmp_capabilities" }') if max_ram_hotplug_size > 0:
data = so.recv(1024) qemu_wrapper.updateDevice({
'device': 'memory',
# Set VNC password 'mem': ram_hotplug_amount * slot_hotplug_size,
so.send('{ "execute": "change", ' \ 'slot': slot_hotplug_size,
'"arguments": { "device": "vnc", "target": "password", ' \ 'nslot': max_ram_hotplug_size / slot_hotplug_size
' "arg": "' + vnc_password + '" } }') })
data = so.recv(1024) except ValueError, e:
with open(status_path, 'w') as status_file:
# Finish status_file.write(str(e))
so.close() raise
with open(status_path, 'w') as status_file: with open(status_path, 'w') as status_file:
status_file.write("OK") status_file.write("")
if __name__ == "__main__":
update()
#!{{ dash }} #!{{ dash }}
if [ -f "{{ qemu_ready_path }}" ]; then FILE="{{ qemu_ready_path }}"
echo "VM correctly started." # don't start checks too fast
sleep 2
if [ -f "$FILE" ]; then
if [ "$(cat $FILE)" = "" ]; then
echo "VM correctly started."
else
>&2 echo "Qemu Controller failed"
>&2 cat $FILE
exit 1
fi
else else
log_file="{{ qemu_service_log_file }}" log_file="{{ qemu_service_log_file }}"
>&2 echo "Qemu process is not correctly started." >&2 echo "Qemu process is not correctly started."
......
...@@ -39,13 +39,15 @@ tap_interface = '{{ parameter_dict.get("tap-interface") }}' ...@@ -39,13 +39,15 @@ tap_interface = '{{ parameter_dict.get("tap-interface") }}'
listen_ip = '{{ parameter_dict.get("ipv4") }}' listen_ip = '{{ parameter_dict.get("ipv4") }}'
mac_address = '{{ parameter_dict.get("mac-address") }}' mac_address = '{{ parameter_dict.get("mac-address") }}'
tap_mac_address = '{{ parameter_dict.get("tap-mac-address") }}' tap_mac_address = '{{ parameter_dict.get("tap-mac-address") }}'
smp_count = '{{ parameter_dict.get("smp-count") }}' smp_count = {{ parameter_dict.get("smp-count") }}
smp_options = '{{ parameter_dict.get("smp-options") }}'.strip() max_smp_hotplug_count = {{ parameter_dict.get("max-smp-hotplug-count") }}
numa_list = '{{ parameter_dict.get("numa") }}'.split() numa_list = '{{ parameter_dict.get("numa", "") }}'.split()
ram_size = '{{ parameter_dict.get("ram-size") }}' ram_size = {{ parameter_dict.get("ram-size") }}
max_ram_hotplug_size = {{ parameter_dict.get("max-ram-hotplug-size") }}
slot_hotplug_size = {{ parameter_dict.get("hotplug-slot-size") }}
pid_file_path = '{{ parameter_dict.get("pid-file-path") }}' pid_file_path = '{{ parameter_dict.get("pid-file-path") }}'
external_disk_number = {{ parameter_dict.get("external-disk-number") }} external_disk_number = {{ parameter_dict.get("external-disk-number") }}
external_disk_size = '{{ parameter_dict.get("external-disk-size") }}' external_disk_size = {{ parameter_dict.get("external-disk-size") }}
external_disk_format = '{{ parameter_dict.get("external-disk-format") }}' external_disk_format = '{{ parameter_dict.get("external-disk-format") }}'
disk_storage_dict = {} disk_storage_dict = {}
disk_storage_list = """{{ parameter_dict.get("disk-storage-list") }}""".split('\n') disk_storage_list = """{{ parameter_dict.get("disk-storage-list") }}""".split('\n')
...@@ -144,6 +146,12 @@ def getMapStorageList(disk_storage_dict, external_disk_number): ...@@ -144,6 +146,12 @@ def getMapStorageList(disk_storage_dict, external_disk_number):
lf.write('%s' % external_disk_number) lf.write('%s' % external_disk_number)
return id_list, external_disk_number return id_list, external_disk_number
def getMemHotplugDict(max_mem_size, slot_size):
if max_mem_size == 0:
return None
number = max_mem_size / slot_size
return {'maxmem': max_mem_size + ram_size, 'slots': number}
# Download existing hard drive if needed at first boot # Download existing hard drive if needed at first boot
if not os.path.exists(disk_path) and virtual_hard_drive_url != '': if not os.path.exists(disk_path) and virtual_hard_drive_url != '':
print('Downloading virtual hard drive...') print('Downloading virtual hard drive...')
...@@ -255,15 +263,18 @@ if use_tap == 'true': ...@@ -255,15 +263,18 @@ if use_tap == 'true':
tap_interface, vhost), tap_interface, vhost),
'-device', 'virtio-net-pci,netdev=lan%s,mac=%s' % (number, tap_mac_address)] '-device', 'virtio-net-pci,netdev=lan%s,mac=%s' % (number, tap_mac_address)]
smp = smp_count if not max_smp_hotplug_count >= 0:
if smp_options: raise ValueError("Max CPU hotpluggable count is not a valid interger")
for option in smp_options.split(','): smp ='%s,maxcpus=%s' % (smp_count, smp_count + max_smp_hotplug_count)
key, val = option.split('=')
if key in ('cores', 'threads', 'sockets', 'maxcpus') and val.isdigit(): mem_dict = getMemHotplugDict(max_ram_hotplug_size, slot_hotplug_size)
smp += ',%s=%s' % (key, val) if mem_dict is not None:
ram_string = '%sM,slots=%s,maxmem=%sM' % (ram_size, mem_dict['slots'], mem_dict['maxmem'])
else:
ram_string = '%s' % ram_size
kvm_argument_list = [qemu_path, kvm_argument_list = [qemu_path,
'-enable-kvm', '-smp', smp, '-name', vm_name, '-enable-kvm', '-smp', smp, '-name', vm_name,
'-m', ram_size, '-vga', 'std', '-m', ram_string, '-vga', 'std',
'-drive', 'file=%s,if=%s,cache=%s,aio=%s' % (disk_path, disk_type, disk_cache, disk_aio), '-drive', 'file=%s,if=%s,cache=%s,aio=%s' % (disk_path, disk_type, disk_cache, disk_aio),
'-vnc', '%s:1,ipv4,password' % listen_ip, '-vnc', '%s:1,ipv4,password' % listen_ip,
'-boot', 'order=cd,menu=on', '-boot', 'order=cd,menu=on',
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment