Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
S
slapos.toolbox
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Gabriel Monnerat
slapos.toolbox
Commits
81d58495
Commit
81d58495
authored
7 years ago
by
Alain Takoudjou
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
qemu qmp: add support for cpu and memory hotplug in qemu
parent
d8ef2aa7
Changes
2
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
840 additions
and
15 deletions
+840
-15
slapos/qemuqmpclient/__init__.py
slapos/qemuqmpclient/__init__.py
+334
-15
slapos/test/test_qemuqmpclient.py
slapos/test/test_qemuqmpclient.py
+506
-0
No files found.
slapos/qemuqmpclient/__init__.py
View file @
81d58495
...
...
@@ -31,6 +31,7 @@ import os
import
pprint
import
socket
import
time
from
operator
import
itemgetter
def
parseArgument
():
"""
...
...
@@ -45,11 +46,18 @@ def parseArgument():
parser
.
add_argument
(
'--delete-internal-snapshot'
,
action
=
'store_const'
,
dest
=
'action'
,
const
=
'deleteInternalSnapshot'
)
parser
.
add_argument
(
'--drive-backup'
,
action
=
'store_const'
,
dest
=
'action'
,
const
=
'driveBackup'
)
parser
.
add_argument
(
'--query-commands'
,
action
=
'store_const'
,
dest
=
'action'
,
const
=
'queryCommands'
)
parser
.
add_argument
(
'--query-item'
,
dest
=
'query'
,
choices
=
[
"cpus"
,
"hotpluggable-cpus"
,
"memory-devices"
,
"memdev"
,
"balloon"
,
"pci"
,
"status"
,
"acpi-ospm-status"
])
parser
.
add_argument
(
'--update-device'
,
action
=
'store_const'
,
dest
=
'action'
,
const
=
'updateDevice'
)
parser
.
add_argument
(
'--device-options'
,
dest
=
'device_options'
,
help
=
"Option used for update-device:
\
n
"
\
'"device=cpu,amount=VALUE_INT[,model=MODEL]"
\
n
OR '
\
'"device=memory,mem=VALUE_MB[,slot=VALUE_MB],nslot=VALUE_INT"'
)
parser
.
add_argument
(
'--socket'
,
dest
=
'unix_socket_location'
,
required
=
True
)
parser
.
add_argument
(
'remainding_argument_list'
,
nargs
=
argparse
.
REMAINDER
)
args
=
parser
.
parse_args
()
return
args
.
unix_socket_location
,
args
.
action
,
args
.
remainding_argument_list
return
parser
.
parse_args
()
class
QemuQMPWrapper
(
object
):
...
...
@@ -58,7 +66,8 @@ class QemuQMPWrapper(object):
See http://git.qemu.org/?p=qemu.git;a=blob;f=qmp-commands.hx for
QMP API definition.
"""
def
__init__
(
self
,
unix_socket_location
):
def
__init__
(
self
,
unix_socket_location
,
auto_connect
=
True
):
if
auto_connect
:
self
.
socket
=
self
.
connectToQemu
(
unix_socket_location
)
self
.
capabilities
()
...
...
@@ -86,11 +95,14 @@ class QemuQMPWrapper(object):
return
so
def
_send
(
self
,
message
):
def
_send
(
self
,
message
,
check_result
=
False
):
self
.
socket
.
send
(
json
.
dumps
(
message
))
data
=
self
.
socket
.
recv
(
65535
)
try
:
return
json
.
loads
(
data
)
result
=
json
.
loads
(
data
)
if
check_result
and
result
.
get
(
'return'
,
None
)
!=
{}
and
'error'
in
data
:
raise
Exception
(
'ERROR: %s'
%
data
)
return
result
except
ValueError
:
# if error the raise
if
"error"
in
data
:
...
...
@@ -98,6 +110,15 @@ class QemuQMPWrapper(object):
else
:
print
'Wrong data: %s'
%
data
def
_sendRetry
(
self
,
message
):
"""
Send Qmp message and retry once if the result is None
"""
result
=
self
.
_send
(
message
)
if
result
is
None
:
return
self
.
_send
(
message
)
return
result
def
_getVMStatus
(
self
):
response
=
self
.
_send
({
'execute'
:
'query-status'
})
if
response
:
...
...
@@ -118,11 +139,29 @@ class QemuQMPWrapper(object):
except
IOError
:
print
'VM not ready, retrying...'
def
capabilities
(
self
):
print
'Asking for capabilities...'
self
.
_send
({
'execute'
:
'qmp_capabilities'
})
def
setVNCPassword
(
self
,
password
):
# Set VNC password
print
'Setting VNC password...'
result
=
self
.
_send
({
"execute"
:
"change"
,
"arguments"
:
{
"device"
:
"vnc"
,
"target"
:
"password"
,
"arg"
:
password
}
})
if
result
and
result
.
get
(
'return'
,
None
)
!=
{}:
raise
ValueError
(
result
)
print
'Done.'
def
powerdown
(
self
):
print
'Stopping the VM...'
self
.
_send
({
'execute'
:
'system_powerdown'
})
def
suspend
(
self
):
print
'Suspending VM...'
self
.
_send
({
'execute'
:
'stop'
})
...
...
@@ -194,17 +233,297 @@ class QemuQMPWrapper(object):
}
})
def
queryCommands
(
self
):
def
getCPUInfo
(
self
):
"""
return some info about VM CPUs
"""
cpu_info_dict
=
{
'hotplugged'
:
[],
'base'
:
[]}
cpu_list
=
self
.
_sendRetry
({
'execute'
:
'query-cpus'
})[
'return'
]
for
cpu
in
cpu_list
:
if
'unattached'
in
cpu
[
'qom_path'
]:
index
=
'base'
else
:
index
=
'hotplugged'
cpu_info_dict
[
index
].
append
({
'props'
:
cpu
[
'props'
],
'CPU'
:
cpu
[
'CPU'
],
'qom_path'
:
cpu
[
'qom_path'
]
})
return
cpu_info_dict
def
getMemoryInfo
(
self
):
"""
return some info about VM Memory. Can only say info about hotplugged RAM
"""
mem_info_dict
=
{
'hotplugged'
:
[],
'base'
:
[]}
memory_list
=
self
.
_sendRetry
({
'execute'
:
'query-memory-devices'
})[
'return'
]
for
mem
in
memory_list
:
if
mem
[
'data'
][
'hotplugged'
]
==
True
:
mem_info_dict
[
'hotplugged'
].
append
(
mem
[
'data'
])
return
mem_info_dict
def
_removeDevice
(
self
,
dev_id
,
command_dict
):
max_retry
=
3
result
=
None
while
max_retry
>
0
and
result
is
None
:
result
=
self
.
_send
(
command_dict
)
max_retry
-=
1
if
(
not
result
or
result
.
get
(
'return'
,
None
)
!=
{})
and
\
max_retry
>
0
:
print
"%s
\
n
Retry remove %r in few seconds..."
%
(
result
,
dev_id
)
time
.
sleep
(
3
)
result
=
None
if
result
is
not
None
:
if
result
.
get
(
'return'
,
None
)
!=
{}:
if
result
.
get
(
'error'
)
and
\
result
[
'error'
].
get
(
'class'
,
''
)
==
'DeviceNotFound'
:
print
'Device %s was removed.'
%
dev_id
else
:
raise
ValueError
(
"Error: Could not remove device %s... %s"
%
(
dev_id
,
result
))
else
:
raise
ValueError
(
"Cannot remove device %s"
%
dev_id
)
def
_updateCPU
(
self
,
amount
,
cpu_model
):
"""
Add or remove CPU according current value
amount: number of CPU to update to
"""
cpu_amount
=
0
empty_socket_list
=
[]
used_socket_id_list
=
[]
unremovable_cpu
=
0
cpu_hotplugable_list
=
self
.
_send
({
'execute'
:
'query-hotpluggable-cpus'
})[
'return'
]
cpu_hotplugable_list
.
reverse
()
for
cpu
in
cpu_hotplugable_list
:
if
cpu
.
get
(
'qom-path'
,
''
)
==
''
:
if
len
(
empty_socket_list
)
<
amount
:
cpu
[
'props'
][
'driver'
]
=
cpu_model
cpu
[
'props'
][
'id'
]
=
'cpu%s'
%
(
cpu
[
'props'
][
'socket-id'
])
empty_socket_list
.
append
(
cpu
[
'props'
])
else
:
# if this is an hotpluggable cpu
if
'/machine/peripheral'
in
cpu
.
get
(
'qom-path'
,
''
):
used_socket_id_list
.
append
(
'cpu%s'
%
(
cpu
[
'props'
][
'socket-id'
]))
cpu_amount
+=
1
else
:
unremovable_cpu
+=
1
hotplug_amount
=
amount
-
unremovable_cpu
# get only hotpluggable CPU
if
hotplug_amount
<
0
:
raise
ValueError
(
"Unattached CPU amount is %s, cannot update to %s"
%
(
unremovable_cpu
,
amount
))
cpu_diff
=
hotplug_amount
-
cpu_amount
max_hotplug_cpu
=
len
(
empty_socket_list
)
if
cpu_amount
==
hotplug_amount
:
# no chanches
print
"Hotplug CPU is up to date."
return
if
cpu_amount
>
hotplug_amount
:
# we will remove CPU
cpu_diff
=
-
1
*
cpu_diff
if
cpu_diff
>=
1
:
print
"Request remove %s CPUs..."
%
cpu_diff
used_socket_id_list
.
reverse
()
for
i
in
range
(
0
,
cpu_diff
):
self
.
_removeDevice
(
used_socket_id_list
[
i
],
{
'execute'
:
'device_del'
,
'arguments'
:
{
'id'
:
used_socket_id_list
[
i
]}
})
elif
cpu_amount
<
hotplug_amount
:
if
max_hotplug_cpu
<
cpu_diff
:
# no hotplugable cpu socket found for Add
raise
ValueError
(
"Cannot Configure %s CPUs, the maximum amount of "
\
"hotplugable CPU is %s!"
%
(
hotplug_amount
,
max_hotplug_cpu
))
print
"Adding %s CPUs..."
%
cpu_diff
for
i
in
range
(
0
,
cpu_diff
):
self
.
_send
({
'execute'
:
'device_add'
,
'arguments'
:
empty_socket_list
[
i
]
},
check_result
=
True
)
# check that hotplugged memery amount is consistent
cpu_info
=
self
.
getCPUInfo
()
final_cpu_count
=
len
(
cpu_info
[
'hotplugged'
])
if
hotplug_amount
!=
final_cpu_count
:
raise
ValueError
(
"Consistency error: Expected %s hotplugged CPU(s) but"
\
" current CPU amount is %s"
%
(
hotplug_amount
,
final_cpu_count
))
print
"Done."
def
_removeMemory
(
self
,
id_dict
):
print
"Trying to remove devices %s, %s..."
%
(
id_dict
[
'id'
],
id_dict
[
'memdev'
])
self
.
_removeDevice
(
id_dict
[
'id'
]
,{
'execute'
:
'device_del'
,
'arguments'
:
{
'id'
:
id_dict
[
'id'
]}
})
# when dimm is removed, remove memdev object
self
.
_removeDevice
(
id_dict
[
'memdev'
],
{
'execute'
:
'object-del'
,
'arguments'
:
{
'id'
:
id_dict
[
'memdev'
]
}
})
def
_updateMemory
(
self
,
mem_size
,
slot_size
,
slot_amount
):
"""
Update memory size according to the current value. option_dict contains:
slot_amount: amount of slots available
mem_size: Size of memory to allocate
slot_size: size of the memory per slot (this value should not change).
default: 512 MB
ex: to add 2G of RAM, mem=2048,slot=512 => allocate 4 slots
to reduce to 1G of RAM, mem=1024,slot=512 => allocate 2 slots
"""
current_size
=
0
num_slot_used
=
0
memory_id_list
=
[]
# current hotplugged memory
cleanup_memdev_id_dict
=
{}
current_dimm_list
=
self
.
_send
({
"execute"
:
"query-memory-devices"
})
current_memdev_list
=
self
.
_send
({
"execute"
:
"query-memdev"
})
for
memdev
in
current_memdev_list
[
'return'
]:
cleanup_memdev_id_dict
[
memdev
[
'id'
]]
=
''
for
dimm
in
current_dimm_list
[
'return'
]:
current_size
+=
dimm
[
'data'
][
'size'
]
if
dimm
[
'data'
][
'hotplugged'
]:
mem_dev
=
os
.
path
.
basename
(
dimm
[
'data'
][
'memdev'
])
cleanup_memdev_id_dict
.
pop
(
mem_dev
)
memory_id_list
.
append
({
'memdev'
:
mem_dev
,
'id'
:
dimm
[
'data'
][
'id'
],
'size'
:
dimm
[
'data'
][
'size'
]
/
(
1024
*
1024
),
})
memory_id_list
=
sorted
(
memory_id_list
,
key
=
itemgetter
(
'id'
))
# cleanup memdev that was not removed because of failure
for
memdev
in
cleanup_memdev_id_dict
.
keys
():
print
"Cleaning up memdev %s..."
%
memdev
self
.
_removeDevice
(
memdev
,
{
'execute'
:
'object-del'
,
'arguments'
:
{
'id'
:
memdev
}
})
num_slot_used
=
len
(
memory_id_list
)
if
num_slot_used
>
0
and
slot_size
!=
memory_id_list
[
0
][
'size'
]:
# XXX - we won't change the defined size of RAM on slots on live,
# restart qemu will allow to change the value
self
.
powerdown
()
raise
ValueError
(
"The Size of RAM Slot changed. Rebooting..."
)
if
(
mem_size
%
slot_size
)
!=
0
:
raise
ValueError
(
"Memory size %r is not a multiple of %r"
%
(
mem_size
,
slot_size
))
if
(
mem_size
/
slot_size
)
>
slot_amount
:
raise
ValueError
(
"No enough slots available to add %sMB of RAM"
%
mem_size
)
current_size
=
current_size
/
(
1024
*
1024
)
if
current_size
==
mem_size
:
print
"Hotplug Memory size is up to date."
return
if
mem_size
<
0
:
raise
ValueError
(
"Memory size is not valid: %s"
%
option_dict
)
elif
current_size
>
mem_size
:
# Request to remove memory
slot_remove
=
(
current_size
-
mem_size
)
/
slot_size
print
"Removing %s memory slots of %s MB..."
%
(
slot_remove
,
slot_size
)
for
i
in
range
(
num_slot_used
,
(
num_slot_used
-
slot_remove
),
-
1
):
# remove all slot that won't be used
self
.
_removeMemory
(
memory_id_list
[
i
-
1
])
elif
current_size
<
mem_size
:
# ask for increase memory
slot_add
=
(
mem_size
-
current_size
)
/
slot_size
print
"Adding %s memory slots of %s MB..."
%
(
slot_add
,
slot_size
)
for
i
in
range
(
0
,
slot_add
):
index
=
num_slot_used
+
i
+
1
self
.
_send
({
'execute'
:
'object-add'
,
'arguments'
:
{
'qom-type'
:
'memory-backend-ram'
,
'id'
:
'mem%s'
%
index
,
'props'
:
{
'size'
:
slot_size
*
1024
*
1024
}
}
})
self
.
_send
({
'execute'
:
'device_add'
,
'arguments'
:
{
'driver'
:
'pc-dimm'
,
'id'
:
'dimm%s'
%
index
,
'memdev'
:
'mem%s'
%
index
}
},
check_result
=
True
)
# check that hotplugged memery amount is consistent
mem_info
=
self
.
getMemoryInfo
()
final_mem_size
=
0
for
mem
in
mem_info
[
'hotplugged'
]:
final_mem_size
+=
mem
[
'size'
]
final_mem_size
=
final_mem_size
/
(
1024
*
1024
)
# get size in MB
if
mem_size
!=
final_mem_size
:
raise
ValueError
(
"Consistency error: Expected %s MB of hotplugged RAM "
\
"but current RAM size is %s MB"
%
(
mem_size
,
final_mem_size
))
print
"Done."
def
updateDevice
(
self
,
option_dict
):
argument_dict
=
{}
if
option_dict
.
has_key
(
'device'
):
if
option_dict
[
'device'
]
==
'cpu'
:
return
self
.
_updateCPU
(
amount
=
int
(
option_dict
[
'amount'
]),
cpu_model
=
option_dict
.
get
(
'model'
,
'qemu64-x86_64-cpu'
)
)
elif
option_dict
[
'device'
]
==
'memory'
:
return
self
.
_updateMemory
(
mem_size
=
int
(
option_dict
[
'mem'
]),
slot_size
=
int
(
option_dict
[
'slot'
]),
slot_amount
=
int
(
option_dict
[
'nslot'
])
)
else
:
raise
ValueError
(
"Unknown device type: %s"
%
option_dict
)
else
:
raise
ValueError
(
"Options are unknown: %s"
%
option_dict
)
def
queryCommands
(
self
,
query
=
None
):
if
query
is
not
None
:
pprint
.
pprint
(
self
.
_send
({
'execute'
:
'query-%s'
%
query
})[
'return'
])
else
:
pprint
.
pprint
(
self
.
_send
({
'execute'
:
'query-commands'
})[
'return'
])
def
main
():
unix_socket_location
,
action
,
remainding_argument_list
=
parseArgument
()
qemu_wrapper
=
QemuQMPWrapper
(
unix_socket_location
)
config
=
parseArgument
()
qemu_wrapper
=
QemuQMPWrapper
(
config
.
unix_socket_location
)
if
remainding_argument_list
:
getattr
(
qemu_wrapper
,
action
)(
*
remainding_argument_list
)
if
config
.
remainding_argument_list
:
getattr
(
qemu_wrapper
,
config
.
action
)(
*
config
.
remainding_argument_list
)
else
:
if
config
.
query
!=
None
:
getattr
(
qemu_wrapper
,
config
.
action
)(
**
{
"query"
:
config
.
query
})
elif
config
.
action
==
"updateDevice"
:
argument_dict
=
{}
for
parameter
in
config
.
device_options
.
split
(
','
):
parameter_list
=
parameter
.
split
(
'='
)
argument_dict
[
parameter_list
[
0
].
strip
()]
=
parameter_list
[
1
].
strip
()
getattr
(
qemu_wrapper
,
config
.
action
)(
argument_dict
)
else
:
getattr
(
qemu_wrapper
,
action
)()
getattr
(
qemu_wrapper
,
config
.
action
)()
if
__name__
==
'__main__'
:
main
()
...
...
This diff is collapsed.
Click to expand it.
slapos/test/test_qemuqmpclient.py
0 → 100644
View file @
81d58495
# -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (c) 2018 Vifib SARL and Contributors. All Rights Reserved.
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# 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, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
import
unittest
import
os
import
tempfile
import
shutil
from
slapos.qemuqmpclient
import
QemuQMPWrapper
class
TestQemuQMPWrapper
(
unittest
.
TestCase
):
def
setUp
(
self
):
self
.
base_dir
=
tempfile
.
mkdtemp
()
self
.
socket_file
=
os
.
path
.
join
(
self
.
base_dir
,
'qmp.socket'
)
self
.
call_stack_list
=
[]
self
.
free_cpu_slot_amount
=
4
self
.
hotplugged_memory_amount
=
0
# slot of 1G
self
.
memory_slot_size
=
1024
def
tearDown
(
self
):
if
os
.
path
.
exists
(
self
.
base_dir
):
shutil
.
rmtree
(
self
.
base_dir
)
def
setChange
(
self
,
device
,
value
):
current
=
self
.
readChange
(
device
)
with
open
(
os
.
path
.
join
(
self
.
base_dir
,
device
),
'w'
)
as
f
:
f
.
write
(
'%s'
%
(
current
+
value
,
))
def
readChange
(
self
,
device
):
if
os
.
path
.
exists
(
os
.
path
.
join
(
self
.
base_dir
,
device
)):
with
open
(
os
.
path
.
join
(
self
.
base_dir
,
device
))
as
f
:
return
int
(
f
.
read
())
return
0
def
fake_send
(
self
,
message
,
check_result
=
False
):
self
.
call_stack_list
.
append
(
message
)
if
message
.
get
(
'execute'
,
''
).
startswith
(
'query-'
):
return
self
.
returnQueryResult
(
message
)
elif
message
.
get
(
'execute'
,
''
)
==
'device_add'
:
if
message
[
'arguments'
][
'driver'
]
==
'pc-dimm'
:
self
.
setChange
(
'dimm'
,
self
.
memory_slot_size
)
elif
message
[
'arguments'
][
'driver'
]
==
'qemu64-x86_64-cpu'
:
self
.
setChange
(
'cpu'
,
1
)
elif
message
.
get
(
'execute'
,
''
)
==
'device_del'
:
if
message
[
'arguments'
][
'id'
].
startswith
(
'dimm'
):
self
.
setChange
(
'dimm'
,
-
1
*
self
.
memory_slot_size
)
if
message
[
'arguments'
][
'id'
].
startswith
(
'cpu'
):
self
.
setChange
(
'cpu'
,
-
1
)
return
{
"return"
:
{}}
def
returnQueryResult
(
self
,
message
):
if
message
[
'execute'
]
==
'query-hotpluggable-cpus'
:
# return 4 hotpluggable cpu slots
hotpluggable_cpu_list
=
[]
free_cpu_slot
=
self
.
free_cpu_slot_amount
-
self
.
readChange
(
'cpu'
)
for
i
in
range
(
4
,
4
-
free_cpu_slot
,
-
1
):
hotpluggable_cpu_list
.
append
({
u'props'
:
{
u'core-id'
:
0
,
u'node-id'
:
0
,
u'socket-id'
:
i
,
u'thread-id'
:
0
},
u'type'
:
u'qemu64-x86_64-cpu'
,
u'vcpus-count'
:
1
})
for
i
in
range
(
4
-
free_cpu_slot
,
0
,
-
1
):
hotpluggable_cpu_list
.
append
({
u'props'
:
{
u'core-id'
:
0
,
u'node-id'
:
0
,
u'socket-id'
:
i
,
u'thread-id'
:
0
},
u'qom-path'
:
u'/machine/peripheral/cpu%s'
%
i
,
u'type'
:
u'qemu64-x86_64-cpu'
,
u'vcpus-count'
:
1
})
# first cpu
hotpluggable_cpu_list
.
append
(
{
u'props'
:
{
u'core-id'
:
0
,
u'node-id'
:
0
,
u'socket-id'
:
0
,
u'thread-id'
:
0
},
u'qom-path'
:
u'/machine/unattached/device[0]'
,
u'type'
:
u'qemu64-x86_64-cpu'
,
u'vcpus-count'
:
1
}
)
return
{
"return"
:
hotpluggable_cpu_list
}
elif
message
[
'execute'
]
==
'query-cpus'
:
cpu_list
=
[]
cpu_slot
=
4
-
self
.
free_cpu_slot_amount
+
self
.
readChange
(
'cpu'
)
cpu_list
.
append
({
u'CPU'
:
0
,
u'arch'
:
u'x86'
,
u'current'
:
True
,
u'halted'
:
True
,
u'pc'
:
-
1694470494
,
u'props'
:
{
u'core-id'
:
0
,
u'node-id'
:
0
,
u'socket-id'
:
0
,
u'thread-id'
:
0
},
u'qom_path'
:
u'/machine/unattached/device[0]'
,
u'thread_id'
:
1181
})
for
i
in
range
(
0
,
cpu_slot
):
cpu_list
.
append
({
u'CPU'
:
i
+
1
,
u'arch'
:
u'x86'
,
u'current'
:
False
,
u'halted'
:
True
,
u'pc'
:
-
1694470494
,
u'props'
:
{
u'core-id'
:
0
,
u'node-id'
:
0
,
u'socket-id'
:
i
+
1
,
u'thread-id'
:
0
},
u'qom_path'
:
u'/machine/peripheral/cpu%s'
%
(
i
+
1
),
u'thread_id'
:
1187
})
return
{
"return"
:
cpu_list
}
elif
message
[
'execute'
]
==
'query-memory-devices'
:
memory_list
=
[]
added_mem
=
self
.
readChange
(
'dimm'
)
+
self
.
hotplugged_memory_amount
slot_amount
=
added_mem
/
self
.
memory_slot_size
for
i
in
range
(
slot_amount
,
0
,
-
1
):
memory_list
.
append
({
u'data'
:
{
u'addr'
:
4563402752
,
u'hotpluggable'
:
True
,
u'hotplugged'
:
True
,
u'id'
:
u'dimm%s'
%
i
,
u'memdev'
:
u'/objects/mem%s'
%
i
,
u'node'
:
0
,
u'size'
:
self
.
memory_slot_size
*
1024
*
1024
,
u'slot'
:
1
},
u'type'
:
u'dimm'
})
return
{
"return"
:
memory_list
}
elif
message
[
'execute'
]
==
'query-memdev'
:
memory_list
=
[]
added_mem
=
self
.
readChange
(
'dimm'
)
+
self
.
hotplugged_memory_amount
slot_amount
=
added_mem
/
self
.
memory_slot_size
for
i
in
range
(
slot_amount
,
0
,
-
1
):
memory_list
.
append
({
u'dump'
:
True
,
u'host-nodes'
:
[],
u'id'
:
u'mem%s'
%
i
,
u'merge'
:
True
,
u'policy'
:
u'default'
,
u'prealloc'
:
False
,
u'size'
:
self
.
memory_slot_size
*
1024
*
1024
})
return
{
"return"
:
memory_list
}
return
{
"return"
:
{}}
def
test_setVNCPassword
(
self
):
qmpwrapper
=
QemuQMPWrapper
(
self
.
socket_file
,
auto_connect
=
False
)
qmpwrapper
.
_send
=
self
.
fake_send
expected_result
=
{
"execute"
:
"change"
,
"arguments"
:
{
"device"
:
"vnc"
,
"target"
:
"password"
,
"arg"
:
"my password"
}
}
qmpwrapper
.
setVNCPassword
(
"my password"
)
self
.
assertEquals
(
len
(
self
.
call_stack_list
),
1
)
self
.
assertEquals
(
self
.
call_stack_list
[
0
],
expected_result
)
def
test_updateDevice_cpu_add
(
self
):
self
.
free_cpu_slot_amount
=
4
qmpwrapper
=
QemuQMPWrapper
(
self
.
socket_file
,
auto_connect
=
False
)
qmpwrapper
.
_send
=
self
.
fake_send
# add 2 cpu
cpu_option
=
{
'device'
:
'cpu'
,
'amount'
:
2
+
1
}
qmpwrapper
.
updateDevice
(
cpu_option
)
expected_result
=
[
{
'execute'
:
'query-hotpluggable-cpus'
},
{
'execute'
:
'device_add'
,
'arguments'
:
{
u'socket-id'
:
1
,
u'thread-id'
:
0
,
'driver'
:
'qemu64-x86_64-cpu'
,
u'core-id'
:
0
,
u'node-id'
:
0
,
'id'
:
'cpu1'
}
},
{
'execute'
:
'device_add'
,
'arguments'
:
{
u'socket-id'
:
2
,
u'thread-id'
:
0
,
'driver'
:
'qemu64-x86_64-cpu'
,
u'core-id'
:
0
,
u'node-id'
:
0
,
'id'
:
'cpu2'
}
},
{
'execute'
:
'query-cpus'
}
]
self
.
assertEquals
(
len
(
self
.
call_stack_list
),
4
)
self
.
assertEquals
(
self
.
call_stack_list
,
expected_result
)
def
test_updateDevice_cpu_increase
(
self
):
self
.
free_cpu_slot_amount
=
2
qmpwrapper
=
QemuQMPWrapper
(
self
.
socket_file
,
auto_connect
=
False
)
qmpwrapper
.
_send
=
self
.
fake_send
# add 2 more cpu
cpu_option
=
{
'device'
:
'cpu'
,
'amount'
:
4
+
1
}
qmpwrapper
.
updateDevice
(
cpu_option
)
expected_result
=
[
{
'execute'
:
'query-hotpluggable-cpus'
},
{
'execute'
:
'device_add'
,
'arguments'
:
{
u'socket-id'
:
3
,
u'thread-id'
:
0
,
'driver'
:
'qemu64-x86_64-cpu'
,
u'core-id'
:
0
,
u'node-id'
:
0
,
'id'
:
'cpu3'
}
},
{
'execute'
:
'device_add'
,
'arguments'
:
{
u'socket-id'
:
4
,
u'thread-id'
:
0
,
'driver'
:
'qemu64-x86_64-cpu'
,
u'core-id'
:
0
,
u'node-id'
:
0
,
'id'
:
'cpu4'
}
},
{
'execute'
:
'query-cpus'
}
]
self
.
assertEquals
(
len
(
self
.
call_stack_list
),
4
)
self
.
assertEquals
(
self
.
call_stack_list
,
expected_result
)
def
test_updateDevice_cpu_remove
(
self
):
self
.
free_cpu_slot_amount
=
2
qmpwrapper
=
QemuQMPWrapper
(
self
.
socket_file
,
auto_connect
=
False
)
qmpwrapper
.
_send
=
self
.
fake_send
# add 2 more cpu
cpu_option
=
{
'device'
:
'cpu'
,
'amount'
:
1
+
1
}
qmpwrapper
.
updateDevice
(
cpu_option
)
expected_result
=
[
{
'execute'
:
'query-hotpluggable-cpus'
},
{
'execute'
:
'device_del'
,
'arguments'
:
{
'id'
:
'cpu2'
}
},
{
'execute'
:
'query-cpus'
}
]
self
.
assertEquals
(
len
(
self
.
call_stack_list
),
3
)
self
.
assertEquals
(
self
.
call_stack_list
,
expected_result
)
def
test_updateDevice_cpu_no_update
(
self
):
self
.
free_cpu_slot_amount
=
2
qmpwrapper
=
QemuQMPWrapper
(
self
.
socket_file
,
auto_connect
=
False
)
qmpwrapper
.
_send
=
self
.
fake_send
# keep 2 cpu added
cpu_option
=
{
'device'
:
'cpu'
,
'amount'
:
2
+
1
}
qmpwrapper
.
updateDevice
(
cpu_option
)
expected_result
=
[
{
'execute'
:
'query-hotpluggable-cpus'
}
]
self
.
assertEquals
(
len
(
self
.
call_stack_list
),
1
)
self
.
assertEquals
(
self
.
call_stack_list
,
expected_result
)
def
test_updateDevice_memory_add
(
self
):
self
.
hotplugged_memory_amount
=
0
qmpwrapper
=
QemuQMPWrapper
(
self
.
socket_file
,
auto_connect
=
False
)
qmpwrapper
.
_send
=
self
.
fake_send
# slot of 1G
self
.
memory_slot_size
=
1024
# add 2G of RAM = 2 slots to add
cpu_option
=
{
'device'
:
'memory'
,
'nslot'
:
4
,
'mem'
:
2048
,
'slot'
:
self
.
memory_slot_size
}
qmpwrapper
.
updateDevice
(
cpu_option
)
expected_result
=
[
{
'execute'
:
'query-memory-devices'
},
{
'execute'
:
'query-memdev'
},
{
'execute'
:
'object-add'
,
'arguments'
:
{
'id'
:
'mem1'
,
'qom-type'
:
'memory-backend-ram'
,
'props'
:
{
'size'
:
self
.
memory_slot_size
*
1024
*
1024
}
}
},
{
'execute'
:
'device_add'
,
'arguments'
:
{
'driver'
:
'pc-dimm'
,
'id'
:
'dimm1'
,
'memdev'
:
'mem1'
}
},
{
'execute'
:
'object-add'
,
'arguments'
:
{
'id'
:
'mem2'
,
'qom-type'
:
'memory-backend-ram'
,
'props'
:
{
'size'
:
1073741824
}
}
},
{
'execute'
:
'device_add'
,
'arguments'
:
{
'driver'
:
'pc-dimm'
,
'id'
:
'dimm2'
,
'memdev'
:
'mem2'
}
},
{
'execute'
:
'query-memory-devices'
}
]
self
.
assertEquals
(
len
(
self
.
call_stack_list
),
7
)
self
.
assertEquals
(
self
.
call_stack_list
,
expected_result
)
def
test_updateDevice_memory_increase
(
self
):
qmpwrapper
=
QemuQMPWrapper
(
self
.
socket_file
,
auto_connect
=
False
)
qmpwrapper
.
_send
=
self
.
fake_send
self
.
hotplugged_memory_amount
=
2048
# slot of 1G
self
.
memory_slot_size
=
1024
# increase to 3G, add one more slot of 1G
cpu_option
=
{
'device'
:
'memory'
,
'nslot'
:
4
,
'mem'
:
3072
,
'slot'
:
self
.
memory_slot_size
}
qmpwrapper
.
updateDevice
(
cpu_option
)
expected_result
=
[
{
'execute'
:
'query-memory-devices'
},
{
'execute'
:
'query-memdev'
},
{
'execute'
:
'object-add'
,
'arguments'
:
{
'id'
:
'mem3'
,
'qom-type'
:
'memory-backend-ram'
,
'props'
:
{
'size'
:
self
.
memory_slot_size
*
1024
*
1024
}
}
},
{
'execute'
:
'device_add'
,
'arguments'
:
{
'driver'
:
'pc-dimm'
,
'id'
:
'dimm3'
,
'memdev'
:
'mem3'
}
},
{
'execute'
:
'query-memory-devices'
}
]
self
.
assertEquals
(
len
(
self
.
call_stack_list
),
5
)
self
.
assertEquals
(
self
.
call_stack_list
,
expected_result
)
def
test_updateDevice_memory_delete
(
self
):
qmpwrapper
=
QemuQMPWrapper
(
self
.
socket_file
,
auto_connect
=
False
)
qmpwrapper
.
_send
=
self
.
fake_send
self
.
hotplugged_memory_amount
=
3072
# slot of 1G
self
.
memory_slot_size
=
1024
# decrease memory to 1G, expext remove slot 3 and 2.
cpu_option
=
{
'device'
:
'memory'
,
'nslot'
:
4
,
'mem'
:
1024
,
'slot'
:
self
.
memory_slot_size
}
qmpwrapper
.
updateDevice
(
cpu_option
)
expected_result
=
[
{
'execute'
:
'query-memory-devices'
},
{
'execute'
:
'query-memdev'
},
{
'execute'
:
'device_del'
,
'arguments'
:
{
'id'
:
u'dimm3'
}
},
{
'execute'
:
'object-del'
,
'arguments'
:
{
'id'
:
u'mem3'
}
},
{
'execute'
:
'device_del'
,
'arguments'
:
{
'id'
:
u'dimm2'
}
},
{
'execute'
:
'object-del'
,
'arguments'
:
{
'id'
:
u'mem2'
}
},
{
'execute'
:
'query-memory-devices'
}
]
self
.
assertEquals
(
len
(
self
.
call_stack_list
),
7
)
self
.
assertEquals
(
self
.
call_stack_list
,
expected_result
)
def
test_updateDevice_memory_delete_all
(
self
):
qmpwrapper
=
QemuQMPWrapper
(
self
.
socket_file
,
auto_connect
=
False
)
qmpwrapper
.
_send
=
self
.
fake_send
self
.
hotplugged_memory_amount
=
3072
# slot of 1G
self
.
memory_slot_size
=
1024
# remove all hotplugged memory
cpu_option
=
{
'device'
:
'memory'
,
'nslot'
:
4
,
'mem'
:
0
,
'slot'
:
self
.
memory_slot_size
}
qmpwrapper
.
updateDevice
(
cpu_option
)
expected_result
=
[
{
'execute'
:
'query-memory-devices'
},
{
'execute'
:
'query-memdev'
},
{
'execute'
:
'device_del'
,
'arguments'
:
{
'id'
:
u'dimm3'
}
},
{
'execute'
:
'object-del'
,
'arguments'
:
{
'id'
:
u'mem3'
}
},
{
'execute'
:
'device_del'
,
'arguments'
:
{
'id'
:
u'dimm2'
}
},
{
'execute'
:
'object-del'
,
'arguments'
:
{
'id'
:
u'mem2'
}
},
{
'execute'
:
'device_del'
,
'arguments'
:
{
'id'
:
u'dimm1'
}
},
{
'execute'
:
'object-del'
,
'arguments'
:
{
'id'
:
u'mem1'
}
},
{
'execute'
:
'query-memory-devices'
}
]
self
.
assertEquals
(
len
(
self
.
call_stack_list
),
9
)
self
.
assertEquals
(
self
.
call_stack_list
,
expected_result
)
def
test_updateDevice_memory_no_update
(
self
):
qmpwrapper
=
QemuQMPWrapper
(
self
.
socket_file
,
auto_connect
=
False
)
qmpwrapper
.
_send
=
self
.
fake_send
self
.
hotplugged_memory_amount
=
3072
# slot of 1G
self
.
memory_slot_size
=
1024
# no changes
cpu_option
=
{
'device'
:
'memory'
,
'nslot'
:
4
,
'mem'
:
self
.
hotplugged_memory_amount
,
'slot'
:
self
.
memory_slot_size
}
qmpwrapper
.
updateDevice
(
cpu_option
)
expected_result
=
[
{
'execute'
:
'query-memory-devices'
},
{
'execute'
:
'query-memdev'
}
]
self
.
assertEquals
(
len
(
self
.
call_stack_list
),
2
)
self
.
assertEquals
(
self
.
call_stack_list
,
expected_result
)
if
__name__
==
'__main__'
:
unittest
.
main
()
This diff is collapsed.
Click to expand it.
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment