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
Jan 18, 2018
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
Hide 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,9 +66,10 @@ 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
):
self
.
socket
=
self
.
connectToQemu
(
unix_socket_location
)
self
.
capabilities
()
def
__init__
(
self
,
unix_socket_location
,
auto_connect
=
True
):
if
auto_connect
:
self
.
socket
=
self
.
connectToQemu
(
unix_socket_location
)
self
.
capabilities
()
@
staticmethod
def
connectToQemu
(
unix_socket_location
):
...
...
@@ -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
):
pprint
.
pprint
(
self
.
_send
({
'execute'
:
'query-commands'
})[
'return'
])
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
:
getattr
(
qemu_wrapper
,
action
)()
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
,
config
.
action
)()
if
__name__
==
'__main__'
:
main
()
...
...
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
()
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