add pause/resume/memory hotplug

This commit is contained in:
ge 2023-11-09 22:35:19 +03:00
parent 253df30371
commit 0d2a18d1f3
4 changed files with 55 additions and 27 deletions

View File

@ -12,9 +12,9 @@ Run `make serve-docs`. See [Development](#development) below.
- [ ] CDROM - [ ] CDROM
- [ ] cloud-init for provisioning instances - [ ] cloud-init for provisioning instances
- [x] Instance power management - [x] Instance power management
- [ ] Instance pause and resume - [x] Instance pause and resume
- [x] vCPU hotplug - [x] vCPU hotplug
- [ ] Memory hotplug - [x] Memory hotplug
- [x] Hot disk resize [not tested] - [x] Hot disk resize [not tested]
- [ ] CPU topology customization - [ ] CPU topology customization
- [x] CPU customization (emulation mode, model, vendor, features) - [x] CPU customization (emulation mode, model, vendor, features)
@ -27,18 +27,18 @@ Run `make serve-docs`. See [Development](#development) below.
- [ ] Instance resources usage stats - [ ] Instance resources usage stats
- [ ] SSH-keys management - [ ] SSH-keys management
- [x] Setting user passwords in guest [not tested] - [x] Setting user passwords in guest [not tested]
- [ ] LXC
- [x] QCOW2 disks support - [x] QCOW2 disks support
- [ ] ZVOL support - [ ] ZVOL support
- [ ] Network disks support - [ ] Network disks support
- [ ] Images service integration (Images service is not implemented yet) - [ ] Images service integration (Images service is not implemented yet)
- [ ] Manage storage pools - [ ] Manage storage pools
- [ ] Idempotency
- [ ] CLI [in progress]
- [ ] HTTP API
- [ ] Instance migrations
- [ ] Instance snapshots - [ ] Instance snapshots
- [ ] Instance backups - [ ] Instance backups
- [ ] Instance migrations - [ ] LXC
- [ ] Idempotency
- [ ] HTTP API
- [ ] CLI [in progress]
## Development ## Development

View File

@ -219,12 +219,21 @@ def main(session: Session, args: argparse.Namespace) -> None:
case 'reset': case 'reset':
instance = session.get_instance(args.instance) instance = session.get_instance(args.instance)
instance.reset() instance.reset()
case 'pause':
instance = session.get_instance(args.instance)
instance.pause()
case 'resume':
instance = session.get_instance(args.instance)
instance.resume()
case 'status': case 'status':
instance = session.get_instance(args.instance) instance = session.get_instance(args.instance)
print(instance.status) print(instance.status)
case 'setvcpus': case 'setvcpus':
instance = session.get_instance(args.instance) instance = session.get_instance(args.instance)
instance.set_vcpus(args.nvcpus, live=True) instance.set_vcpus(args.nvcpus, live=True)
case 'setmem':
instance = session.get_instance(args.instance)
instance.set_memory(args.memory, live=True)
def cli() -> None: # noqa: PLR0915 def cli() -> None: # noqa: PLR0915
@ -352,6 +361,14 @@ def cli() -> None: # noqa: PLR0915
reset = subparsers.add_parser('reset', help='reset instance') reset = subparsers.add_parser('reset', help='reset instance')
reset.add_argument('instance') reset.add_argument('instance')
# pause subcommand
pause = subparsers.add_parser('pause', help='pause instance')
pause.add_argument('instance')
# resume subcommand
resume = subparsers.add_parser('resume', help='resume paused instance')
resume.add_argument('instance')
# status subcommand # status subcommand
status = subparsers.add_parser('status', help='display instance status') status = subparsers.add_parser('status', help='display instance status')
status.add_argument('instance') status.add_argument('instance')
@ -361,6 +378,11 @@ def cli() -> None: # noqa: PLR0915
setvcpus.add_argument('instance') setvcpus.add_argument('instance')
setvcpus.add_argument('nvcpus', type=int) setvcpus.add_argument('nvcpus', type=int)
# setmem subcommand
setmem = subparsers.add_parser('setmem', help='set memory size')
setmem.add_argument('instance')
setmem.add_argument('memory', type=int, help='memory in MiB')
# Run parser # Run parser
args = root.parse_args() args = root.parse_args()
if args.command is None: if args.command is None:

View File

@ -218,13 +218,13 @@ class Instance:
def get_info(self) -> InstanceInfo: def get_info(self) -> InstanceInfo:
"""Return instance info.""" """Return instance info."""
_info = self.domain.info() info = self.domain.info()
return InstanceInfo( return InstanceInfo(
state=self._expand_instance_state(_info[0]), state=self._expand_instance_state(info[0]),
max_memory=_info[1], max_memory=info[1],
memory=_info[2], memory=info[2],
nproc=_info[3], nproc=info[3],
cputime=_info[4], cputime=info[4],
) )
def get_status(self) -> str: def get_status(self) -> str:
@ -405,10 +405,10 @@ class Instance:
:param nvcpus: Number of vCPUs :param nvcpus: Number of vCPUs
:param live: Affect a running instance :param live: Affect a running instance
""" """
if nvcpus == 0: if nvcpus <= 0:
raise InstanceError( raise InstanceError('Cannot set zero vCPUs')
f'Cannot set zero vCPUs for instance={self.name}' if nvcpus > self.get_max_vcpus():
) raise InstanceError('vCPUs count is greather than max_vcpus')
if nvcpus == self.get_info().nproc: if nvcpus == self.get_info().nproc:
log.warning( log.warning(
'Instance instance=%s already have %s vCPUs, nothing to do', 'Instance instance=%s already have %s vCPUs, nothing to do',
@ -461,11 +461,18 @@ class Instance:
:param memory: Memory value in mebibytes :param memory: Memory value in mebibytes
:param live: Affect a running instance :param live: Affect a running instance
""" """
if memory == 0: if memory <= 0:
raise InstanceError( raise InstanceError('Cannot set zero memory')
f'Cannot set zero memory for instance={self.name}' if (memory * 1024) > self.get_max_memory():
raise InstanceError('Memory is greather than max_memory')
if (memory * 1024) == self.get_info().memory:
log.warning(
"Instance '%s' already have %s memory, nothing to do",
self.name,
memory,
) )
if live and self.info()['state'] == libvirt.VIR_DOMAIN_RUNNING: return
if live and self.is_running():
flags = ( flags = (
libvirt.VIR_DOMAIN_AFFECT_LIVE libvirt.VIR_DOMAIN_AFFECT_LIVE
| libvirt.VIR_DOMAIN_AFFECT_CONFIG | libvirt.VIR_DOMAIN_AFFECT_CONFIG
@ -473,9 +480,6 @@ class Instance:
else: else:
flags = libvirt.VIR_DOMAIN_AFFECT_CONFIG flags = libvirt.VIR_DOMAIN_AFFECT_CONFIG
try: try:
self.domain.setMemoryFlags(
memory * 1024, flags=libvirt.VIR_DOMAIN_MEM_MAXIMUM
)
self.domain.setMemoryFlags(memory * 1024, flags=flags) self.domain.setMemoryFlags(memory * 1024, flags=flags)
except libvirt.libvirtError as e: except libvirt.libvirtError as e:
msg = f'Cannot set memory for instance={self.name} {memory=}: {e}' msg = f'Cannot set memory for instance={self.name} {memory=}: {e}'
@ -535,11 +539,13 @@ class Instance:
def pause(self) -> None: def pause(self) -> None:
"""Pause instance.""" """Pause instance."""
raise NotImplementedError if not self.is_running():
raise InstanceError('Cannot pause inactive instance')
self.domain.suspend()
def resume(self) -> None: def resume(self) -> None:
"""Resume paused instance.""" """Resume paused instance."""
raise NotImplementedError self.domain.resume()
def list_ssh_keys(self, user: str) -> list[str]: def list_ssh_keys(self, user: str) -> list[str]:
""" """

View File

@ -44,7 +44,7 @@ select = ['ALL']
ignore = [ ignore = [
'Q000', 'Q003', 'D211', 'D212', 'ANN101', 'ISC001', 'COM812', 'Q000', 'Q003', 'D211', 'D212', 'ANN101', 'ISC001', 'COM812',
'D203', 'ANN204', 'T201', 'D203', 'ANN204', 'T201',
'EM102', 'TRY003', # maybe not ignore? 'EM102', 'TRY003', 'EM101', # maybe not ignore?
'TD003', 'TD006', 'FIX002', # todo strings linting 'TD003', 'TD006', 'FIX002', # todo strings linting
] ]
exclude = ['__init__.py'] exclude = ['__init__.py']