Commit 2d79471e by Alain Takoudjou

kvm: can now accept parameters external-disk-format, cpu-options and numa

kvm_run script can now create external disk with specified format (qcow2, raw, etc...)
if kvm external disk is created it will never be replaced, even if user
reduce the number of disk with parameter 'external-disk-number' or change the
'external-disk-format' value. All created disk will be added to qemu run command.

numa option can be used to create [fake] numa nodes and expose them to the guest
OS instance. In this case it is important to know the host cpu topology and where
the instance will be allocated.

cpu-options are additional parameters like sockets=1,cores=5,threads=1
in this case if cpu-count parameter is 1 qemu options will be:
-smp 1,sockets=1,cores=5,threads=1
1 parent a2c351d6
......@@ -9,6 +9,8 @@ import subprocess
import urllib
import gzip
import shutil
from random import shuffle
import glob
# XXX: give all of this through parameter, don't use this as template, but as module
qemu_img_path = '%(qemu-img-path)s'
......@@ -30,10 +32,13 @@ listen_ip = '%(ipv4)s'
mac_address = '%(mac-address)s'
tap_mac_address = '%(tap-mac-address)s'
smp_count = '%(smp-count)s'
smp_options = '%(smp-options)s'.strip()
numa_list = '%(numa)s'.split()
ram_size = '%(ram-size)s'
pid_file_path = '%(pid-file-path)s'
external_disk_number = %(external-disk-number)s
external_disk_size = '%(external-disk-size)s'
external_disk_format = '%(external-disk-format)s'
disk_storage_dict = {}
disk_storage_list = """%(disk-storage-list)s""".split('\n')
map_storage_list = []
......@@ -68,21 +73,41 @@ def getSocketStatus(host, port):
break
return s
def getMapStorageList(disk_storage_dict):
def getMapStorageList(disk_storage_dict, external_disk_number):
map_disk_file = os.path.join(etc_directory, '.data-disk-ids')
last_disk_num_f = os.path.join(etc_directory, '.data-disk-amount')
id_list = []
if os.path.exists(map_disk_file):
last_amount = 0
map_f_exist = os.path.exists(map_disk_file)
if os.path.exists(last_disk_num_f):
with open(last_disk_num_f, 'r') as lf:
last_amount = int(lf.readline())
if map_f_exist:
with open(map_disk_file, 'r') as mf:
# ID are writen in one line: data1 data3 data2 ...
content = mf.readline()
if content:
id_list = [id for id in content.split(' ') if disk_storage_dict.has_key(id)]
for id in content.split(' '):
if disk_storage_dict.has_key(id):
id_list.append(id)
else:
# Mean that this disk path has been removed (disk unmounted)
last_amount -= 1
for key in disk_storage_dict:
if not key in id_list:
id_list.append(key)
with open(map_disk_file, 'w') as mf:
mf.write(' '.join(id_list))
return id_list
if id_list:
if not map_f_exist:
# shuffle the list to not write disk in data1, data2, ... everytime
shuffle(id_list)
if external_disk_number < last_amount:
# Drop created disk is not allowed
external_disk_number = last_amount
with open(map_disk_file, 'w') as mf:
mf.write(' '.join(id_list))
with open(last_disk_num_f, 'w') as lf:
lf.write('%%s' %% external_disk_number)
return id_list, external_disk_number
# Download existing hard drive if needed at first boot
if not os.path.exists(disk_path) and virtual_hard_drive_url != '':
......@@ -122,24 +147,34 @@ if not os.path.exists(disk_path):
# Check and create external disk
additional_disk_list = []
for storage in sorted(disk_storage_list):
for storage in disk_storage_list:
if storage:
key, val = storage.split(' ')
disk_storage_dict[key.strip()] = val.strip()
map_storage_list = getMapStorageList(disk_storage_dict)
if not external_disk_format in ['qcow2', 'raw', 'vdi', 'vmdk', 'cloop']:
external_disk_format = 'qcow2'
map_storage_list, external_disk_number = getMapStorageList(disk_storage_dict,
int(external_disk_number))
assert len(map_storage_list) == len(disk_storage_dict)
if disk_storage_dict:
if int(external_disk_number) > 0:
if external_disk_number > 0:
index = 0
while (index < len(disk_storage_dict)) and (index < external_disk_number):
path = disk_storage_dict[map_storage_list[index]]
if os.path.exists(path):
disk_filepath = os.path.join(path, 'kvm_virtual_disk.qcow2')
if not os.path.exists(disk_filepath):
disk_filepath = os.path.join(path,
'kvm_virtual_disk.%%s' %% external_disk_format)
disk_list = glob.glob('%%s.*' %% os.path.join(path, 'kvm_virtual_disk'))
if disk_list == []:
print('Creating one additional virtual hard drive...')
subprocess.Popen([qemu_img_path, 'create' ,'-f', 'qcow2',
subprocess.Popen([qemu_img_path, 'create' ,'-f', '%%s' %% external_disk_format,
disk_filepath, '%%sG' %% external_disk_size])
else:
# Cannot change or recreate if disk is exists
disk_filepath = disk_list[0]
additional_disk_list.append(disk_filepath)
else:
print('Data folder %%s was not used to create external disk %%r' %% (index +1))
......@@ -150,6 +185,7 @@ if disk_storage_dict:
# XXX: use_tap should be a boolean
tap_network_parameter = []
nat_network_parameter = []
numa_parameter = []
number = -1
if use_nat == 'True':
number += 1
......@@ -164,8 +200,11 @@ if use_tap == 'True':
tap_interface),
'-device', 'e1000,netdev=lan%%s,mac=%%s' %% (number, tap_mac_address)]
smp = smp_count
if smp_options:
smp += ',%%s' %% smp_options
kvm_argument_list = [qemu_path,
'-enable-kvm', '-smp', smp_count,
'-enable-kvm', '-smp', smp,
'-m', ram_size, '-vga', 'std',
'-drive', 'file=%%s,if=%%s' %% (disk_path, disk_type),
'-vnc', '%%s:1,ipv4,password' %% listen_ip,
......@@ -173,6 +212,11 @@ kvm_argument_list = [qemu_path,
'-qmp', 'unix:%%s,server' %% socket_path,
'-pidfile', pid_file_path,
]
for numa in numa_list:
numa_parameter.extend(['-numa', numa])
kvm_argument_list += numa_parameter
if tap_network_parameter == [] and nat_network_parameter == []:
print 'Warning : No network interface defined.'
else:
......@@ -201,4 +245,5 @@ else:
'-drive', 'file=%%s,media=cdrom' %% default_disk_image
])
print 'Starting KVM: \n %%s' %% ' '.join(kvm_argument_list)
os.execv(qemu_path, kvm_argument_list)
......@@ -93,7 +93,7 @@ mode = 0644
recipe = hexagonit.recipe.download
url = ${:_profile_base_location_}/instance-kvm.cfg.jinja2
mode = 644
md5sum = 5506e1df6ba32c6ead647636ebece79e
md5sum = c8f69039aff15e20447c6e522267c152
download-only = true
on-update = true
......@@ -101,7 +101,7 @@ on-update = true
recipe = hexagonit.recipe.download
url = ${:_profile_base_location_}/instance-kvm-cluster.cfg.jinja2.in
mode = 644
md5sum = 214c46a9aa7605951b8a1f98572dac28
md5sum = a5c1f0620510a979daf8fd60120f6253
download-only = true
on-update = true
......
......@@ -166,6 +166,16 @@
"minimum": 1,
"maximum": 8
},
"cpu-options": {
"title": "Additional options (cores, threads, sockets, maxcpus) to use with cpu-count.",
"description": "Additional options to use with cpu-count. Options are separated by coma: [cores=cores][,threads=threads][,sockets=sockets][,maxcpus=maxcpus]. Only set this option if you really know what you're doing.",
"type": "string"
},
"numa": {
"title": "Simulate a multi node NUMA system. If mem and cpus are omitted, resources are split equally.",
"description": "Simulate a multi node NUMA system. If mem and cpus are omitted, resources are split equally. Each numa option are separated by space: node,nodeid=4,cpus=40-49,mem=64g node,nodeid=1,cpus=10-19,mem=128g. Only set this option if you really know what you're doing.",
"type": "string"
},
"nbd-host": {
"title": "NBD hostname",
"description": "hostname (or IP) of the NBD server containing the boot image.",
......@@ -235,6 +245,13 @@
"maximum": 100,
"default": 20
},
"external-disk-format": {
"title": "Type of external disk drive to create by QEMU.",
"description": "Type of QEMU disk drive, to create.",
"type": "string",
"default": "qcow2",
"enum": ["qcow2", "raw", "vdi", "vmdk", "cloop"]
},
"use-tap": {
"title": "Use QEMU TAP network interface",
"description": "Use QEMU TAP network interface, might require a bridge on SlapOS Node.",
......
......@@ -38,6 +38,8 @@ config-ram-size = {{ dumps(kvm_parameter_dict.get('ram-size', 1024)) }}
config-disk-size = {{ dumps(kvm_parameter_dict.get('disk-size', 10)) }}
config-disk-type = {{ dumps(kvm_parameter_dict.get('disk-type', 'virtio')) }}
config-cpu-count = {{ dumps(kvm_parameter_dict.get('cpu-count', 1)) }}
config-cpu-options = {{ dumps(kvm_parameter_dict.get('cpu-options', '')) }}
config-numa = {{ dumps(kvm_parameter_dict.get('numa', '')) }}
{% set nat_rules_list = kvm_parameter_dict.get('nat-rules', [22, 80, 443]) -%}
config-nat-rules = {{ nat_rules_list | join(' ') }}
......@@ -49,6 +51,7 @@ config-virtual-hard-drive-md5sum = {{ dumps(kvm_parameter_dict.get('virtual-hard
config-virtual-hard-drive-gzipped = {{ dumps(kvm_parameter_dict.get('virtual-hard-drive-gzipped', False)) }}
config-external-disk-number = {{ dumps(kvm_parameter_dict.get('external-disk-number', 0)) }}
config-external-disk-size = {{ dumps(kvm_parameter_dict.get('external-disk-size', 20)) }}
config-external-disk-format = {{ dumps(kvm_parameter_dict.get('external-disk-format', 'qcow2')) }}
return =
backend-url
url
......
......@@ -36,6 +36,16 @@
"minimum": 1,
"maximum": 8
},
"cpu-options": {
"title": "Additional options (cores, threads, sockets, maxcpus) to use with cpu-count.",
"description": "Additional options to use with cpu-count. Options are separated by coma: [cores=cores][,threads=threads][,sockets=sockets][,maxcpus=maxcpus]. Only set this option if you really know what you're doing.",
"type": "string"
},
"numa": {
"title": "Simulate a multi node NUMA system. If mem and cpus are omitted, resources are split equally.",
"description": "Simulate a multi node NUMA system. If mem and cpus are omitted, resources are split equally. Each numa option are separated by space: node,nodeid=4,cpus=40-49,mem=64g node,nodeid=1,cpus=10-19,mem=128g. Only set this option if you really know what you're doing.",
"type": "string"
},
"nbd-host": {
"title": "NBD hostname",
......@@ -101,6 +111,13 @@
"maximum": 100,
"default": 20
},
"external-disk-format": {
"title": "Type of external disk drive to create by QEMU.",
"description": "Type of QEMU disk drive, to create.",
"type": "string",
"default": "qcow2",
"enum": ["qcow2", "raw", "vdi", "vmdk", "cloop"]
},
"use-tap": {
"title": "Use QEMU TAP network interface",
......
......@@ -81,7 +81,9 @@ socket-path = ${directory:var}/qmp_socket
pid-file-path = ${directory:run}/pid_file
smp-count = ${slap-parameter:cpu-count}
smp-options = ${slap-parameter:cpu-options}
ram-size = ${slap-parameter:ram-size}
numa = ${slap-parameter:numa}
mac-address = ${create-mac:mac-address}
tap-mac-address = ${create-tap-mac:mac-address}
......@@ -110,6 +112,7 @@ disk-storage-list =
{% endfor -%}
external-disk-number = ${slap-parameter:external-disk-number}
external-disk-size = ${slap-parameter:external-disk-size}
external-disk-format = ${slap-parameter:external-disk-format}
[kvm-vnc-promise]
recipe = slapos.cookbook:check_port_listening
......@@ -275,6 +278,10 @@ disk-size = 10
disk-type = virtio
cpu-count = 1
# cpu-option is a string: [cores=cores][,threads=threads][,sockets=sockets][,maxcpus=maxcpus]
cpu-options =
# list of numa options separate by space: node,nodeid=1,cpus=9-15 node,nodeid=2,cpus=1,3,7
numa =
nat-rules = 22 80 443
use-nat = True
......@@ -286,3 +293,4 @@ virtual-hard-drive-gzipped = False
external-disk-number = 0
external-disk-size = 20
external-disk-format = qcow2
Styling with Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!