diff --git a/src/middlewared/middlewared/plugins/virt/instance.py b/src/middlewared/middlewared/plugins/virt/instance.py index b64cad6a45be..8aef9b786204 100644 --- a/src/middlewared/middlewared/plugins/virt/instance.py +++ b/src/middlewared/middlewared/plugins/virt/instance.py @@ -189,11 +189,14 @@ async def do_create(self, job, data): devices = {} for i in (data['devices'] or []): - await self.middleware.call('virt.instance.validate', i, 'virt_instance_create', verrors) + await self.middleware.call('virt.instance.validate_device', i, 'virt_instance_create', verrors) if i['name'] is None: i['name'] = await self.middleware.call('virt.instance.generate_device_name', devices.keys(), i['dev_type']) devices[i['name']] = await self.middleware.call('virt.instance.device_to_incus', data['instance_type'], i) + if not verrors and data['devices']: + await self.middleware.call('virt.instance.validate_devices', data['devices'], 'virt_instance_create', verrors) + verrors.check() async def running_cb(data): diff --git a/src/middlewared/middlewared/plugins/virt/instance_device.py b/src/middlewared/middlewared/plugins/virt/instance_device.py index 9f64bb0c6b87..3c9393b99935 100644 --- a/src/middlewared/middlewared/plugins/virt/instance_device.py +++ b/src/middlewared/middlewared/plugins/virt/instance_device.py @@ -242,7 +242,27 @@ async def generate_device_name(self, device_names: list[str], device_type: str) i += 1 return name - async def __validate_device(self, device, schema, verrors: ValidationErrors, old: dict = None, instance: str = None): + @private + async def validate_devices(self, devices, schema, verrors: ValidationErrors): + unique_src_proxies = [] + unique_dst_proxies = [] + + for device in devices: + match device['dev_type']: + case 'PROXY': + source = (device['source_proto'], device['source_port']) + if source in unique_src_proxies: + verrors.add(f'{schema}.source_port', 'Source proto/port already in use.') + else: + unique_src_proxies.append(source) + dst = (device['dest_proto'], device['dest_port']) + if dst in unique_dst_proxies: + verrors.add(f'{schema}.dest_port', 'Destination proto/port already in use.') + else: + unique_dst_proxies.append(dst) + + @private + async def validate_device(self, device, schema, verrors: ValidationErrors, old: dict = None, instance: str = None): match device['dev_type']: case 'PROXY': # Skip validation if we are updating and port has not changed @@ -283,7 +303,7 @@ async def device_add(self, id, device): device['name'] = await self.generate_device_name(data['devices'].keys(), device['dev_type']) verrors = ValidationErrors() - await self.__validate_device(device, 'virt_device_add', verrors) + await self.validate_device(device, 'virt_device_add', verrors) verrors.check() data['devices'][device['name']] = await self.device_to_incus(instance['type'], device) @@ -305,7 +325,7 @@ async def device_update(self, id, device): raise CallError('Device does not exist.', errno.ENOENT) verrors = ValidationErrors() - await self.__validate_device(device, 'virt_device_update', verrors, old, instance['name']) + await self.validate_device(device, 'virt_device_update', verrors, old, instance['name']) verrors.check() data['devices'][device['name']] = await self.device_to_incus(instance['type'], device)