add pause/resume/memory hotplug
This commit is contained in:
		
							
								
								
									
										14
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								README.md
									
									
									
									
									
								
							@@ -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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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:
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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]:
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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']
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user