Skip to content

Commit

Permalink
Add enterprise license check for virt plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
Qubad786 committed Jan 2, 2025
1 parent e81a3aa commit da3e409
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 3 deletions.
3 changes: 3 additions & 0 deletions src/middlewared/middlewared/plugins/virt/global.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,9 @@ async def validate(self, new: dict, schema_name: str, verrors: ValidationErrors)
if pool == POOL_DISABLED:
new['pool'] = None

if pool and not await self.middleware.call('virt.global.license_active'):
verrors.add(f'{schema_name}.pool', 'System is not licensed to run virtualization')

@api_method(VirtGlobalUpdateArgs, VirtGlobalUpdateResult)
@job()
async def do_update(self, job, data):
Expand Down
10 changes: 7 additions & 3 deletions src/middlewared/middlewared/plugins/virt/instance.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,12 @@ async def query(self, filters, options):
async def validate(self, new, schema_name, verrors, old=None):
# Do not validate image_choices because its an expansive operation, just fail on creation

instance_type = new.get('instance_type') or (old or {}).get('type')
if instance_type and not await self.middleware.call('virt.global.license_active', instance_type):
verrors.add(
f'{schema_name}.instance_type', f'System is not licensed to manage {new["instance_type"]!r} instances'
)

if not old and await self.query([('name', '=', new['name'])]):
verrors.add(f'{schema_name}.name', f'Name {new["name"]!r} already exists')

Expand Down Expand Up @@ -164,9 +170,7 @@ async def validate(self, new, schema_name, verrors, old=None):
'vnc_port': old['vnc_port'],
})

if (
new.get('instance_type') == 'VM' or (old and old['type'] == 'VM')
) and new.get('enable_vnc'):
if instance_type == 'VM' and new.get('enable_vnc'):
if not new.get('vnc_port'):
verrors.add(f'{schema_name}.vnc_port', 'VNC port is required when VNC is enabled')
else:
Expand Down
32 changes: 32 additions & 0 deletions src/middlewared/middlewared/plugins/virt/license.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
from middlewared.service import private, Service


class VirtLicenseGlobalService(Service):

class Config:
namespace = 'virt.global'

@private
async def license_active(self, instance_type=None):
"""
If this is iX enterprise hardware and has NOT been licensed to run virt plugin
then this will return False, otherwise this will return true.
"""
system_chassis = await self.middleware.call('truenas.get_chassis_hardware')
if system_chassis == 'TRUENAS_UNKNOWN' or 'MINI' in system_chassis:
# 1. if it's not iX branded hardware
# 2. OR if it's a MINI, then allow containers/vms
return True

license_ = await self.middleware.call('system.license')
if license_ is None:
# it's iX branded hardware but has no license
return False

if instance_type is None:
# licensed JAILS (containers) and/or VMs
return any(k in license_['features'] for k in ('JAILS', 'VM'))
else:
# license could only have JAILS (containers) licensed or VM
feature = 'JAILS' if instance_type == 'CONTAINER' else 'VM'
return feature in license_['features']
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import pytest

from middlewared.plugins.virt.license import VirtLicenseGlobalService
from middlewared.pytest.unit.middleware import Middleware


@pytest.mark.parametrize('instance_type,chassis_hardware,license_active,expected_result', [
(
None,
'TRUENAS-UNKNOWN',
None,
True
),
(
None,
'TRUENAS-MINI-3.0-XL+',
None,
True
),
(
None,
'TRUENAS-M60-HA',
{'features': ['JAILS', 'VM']},
True
),
(
'CONTAINER',
'TRUENAS-M60-HA',
{'features': ['JAILS', 'VM']},
True
),
(
'VM',
'TRUENAS-M60-HA',
{'features': ['JAILS', 'VM']},
True
),
(
'CONTAINER',
'TRUENAS-M60-HA',
{'features': ['VM']},
False
),
(
'VM',
'TRUENAS-M60-HA',
{'features': ['JAILS']},
False
),
(
None,
'TRUENAS-M60-HA',
None,
False
),
(
'VM',
'TRUENAS-M60-HA',
None,
False
),
])
@pytest.mark.asyncio
async def test_virt_license_validation(instance_type, chassis_hardware, license_active, expected_result):
m = Middleware()
m['truenas.get_chassis_hardware'] = lambda *arg: chassis_hardware
m['system.license'] = lambda *arg: license_active
assert await VirtLicenseGlobalService(m).license_active(instance_type) == expected_result

0 comments on commit da3e409

Please sign in to comment.