436 lines
9.9 KiB
Python
436 lines
9.9 KiB
Python
import json
|
|
import re
|
|
|
|
from datetime import timedelta
|
|
|
|
from django.db import models
|
|
from django.forms import ValidationError
|
|
|
|
from access.fields import *
|
|
from access.models import TenancyObject
|
|
|
|
from app.helpers.merge_software import merge_software
|
|
|
|
from core.mixin.history_save import SaveHistory
|
|
|
|
from itam.models.device_common import DeviceCommonFields, DeviceCommonFieldsName
|
|
from itam.models.device_models import DeviceModel
|
|
from itam.models.software import Software, SoftwareVersion
|
|
from itam.models.operating_system import OperatingSystemVersion
|
|
|
|
from settings.models.app_settings import AppSettings
|
|
|
|
|
|
|
|
class DeviceType(DeviceCommonFieldsName, SaveHistory):
|
|
|
|
|
|
def clean(self):
|
|
|
|
app_settings = AppSettings.objects.get(owner_organization=None)
|
|
|
|
if app_settings.device_type_is_global:
|
|
|
|
self.organization = app_settings.global_organization
|
|
self.is_global = app_settings.device_type_is_global
|
|
|
|
|
|
def __str__(self):
|
|
|
|
return self.name
|
|
|
|
|
|
|
|
class Device(DeviceCommonFieldsName, SaveHistory):
|
|
|
|
|
|
reserved_config_keys: list = [
|
|
'software'
|
|
]
|
|
|
|
def validate_config_keys_not_reserved(self):
|
|
|
|
value: dict = self
|
|
|
|
for invalid_key in Device.reserved_config_keys:
|
|
|
|
if invalid_key in value.keys():
|
|
raise ValidationError(f'json key "{invalid_key}" is a reserved configuration key')
|
|
|
|
|
|
def validate_uuid_format(self):
|
|
|
|
pattern = r'[0-9|a-f]{8}\-[0-9|a-f]{4}\-[0-9|a-f]{4}\-[0-9|a-f]{4}\-[0-9|a-f]{12}'
|
|
|
|
if not re.match(pattern, str(self)):
|
|
|
|
raise ValidationError(f'UUID Must be in {str(pattern)}')
|
|
|
|
|
|
def validate_hostname_format(self):
|
|
|
|
pattern = r'^[a-z]{1}[a-z|0-9|\-]+[a-z|0-9]{1}$'
|
|
|
|
if not re.match(pattern, str(self).lower()):
|
|
|
|
raise ValidationError(
|
|
'''[RFC1035 2.3.1] A hostname must start with a letter, end with a letter or digit,
|
|
and have as interior characters only letters, digits, and hyphen.'''
|
|
)
|
|
|
|
|
|
name = models.CharField(
|
|
blank = False,
|
|
max_length = 50,
|
|
unique = True,
|
|
validators = [ validate_hostname_format ]
|
|
)
|
|
|
|
serial_number = models.CharField(
|
|
verbose_name = 'Serial Number',
|
|
max_length = 50,
|
|
default = None,
|
|
null = True,
|
|
blank = True,
|
|
unique = True,
|
|
help_text = 'Serial number of the device.',
|
|
|
|
)
|
|
|
|
uuid = models.CharField(
|
|
verbose_name = 'UUID',
|
|
max_length = 50,
|
|
default = None,
|
|
null = True,
|
|
blank = True,
|
|
unique = True,
|
|
help_text = 'System GUID/UUID.',
|
|
validators = [ validate_uuid_format ]
|
|
)
|
|
|
|
device_model = models.ForeignKey(
|
|
DeviceModel,
|
|
on_delete=models.CASCADE,
|
|
default = None,
|
|
null = True,
|
|
blank= True,
|
|
help_text = 'Model of the device.',
|
|
)
|
|
|
|
device_type = models.ForeignKey(
|
|
DeviceType,
|
|
on_delete=models.CASCADE,
|
|
default = None,
|
|
null = True,
|
|
blank= True,
|
|
help_text = 'Type of device.',
|
|
)
|
|
|
|
|
|
config = models.JSONField(
|
|
blank = True,
|
|
default = None,
|
|
null = True,
|
|
validators=[ validate_config_keys_not_reserved ],
|
|
verbose_name = 'Host Configuration',
|
|
help_text = 'Configuration for this device'
|
|
)
|
|
|
|
inventorydate = models.DateTimeField(
|
|
verbose_name = 'Last Inventory Date',
|
|
null = True,
|
|
blank = True,
|
|
)
|
|
|
|
def save(
|
|
self, force_insert=False, force_update=False, using=None, update_fields=None
|
|
):
|
|
""" Save Device Model
|
|
|
|
After saving the device update the related items so that they are a part
|
|
of the same organization as the device.
|
|
"""
|
|
|
|
super().save(
|
|
force_insert=False, force_update=False, using=None, update_fields=None
|
|
)
|
|
|
|
models_to_update =[
|
|
DeviceSoftware,
|
|
DeviceOperatingSystem
|
|
]
|
|
|
|
for update_model in models_to_update:
|
|
|
|
obj = update_model.objects.filter(
|
|
device = self.id,
|
|
)
|
|
|
|
if obj.exists():
|
|
|
|
obj.update(
|
|
is_global = False,
|
|
organization = self.organization,
|
|
)
|
|
|
|
from config_management.models.groups import ConfigGroupHosts
|
|
|
|
ConfigGroupHosts.objects.filter(
|
|
host = self.id,
|
|
).delete()
|
|
|
|
|
|
def __str__(self):
|
|
|
|
return self.name
|
|
|
|
@property
|
|
def status(self) -> str:
|
|
""" Fetch Device status
|
|
|
|
Returns:
|
|
str: Current status of the item
|
|
"""
|
|
|
|
if self.inventorydate:
|
|
|
|
check_date = self.inventorydate
|
|
|
|
else:
|
|
|
|
check_date = now() + timedelta(days=99)
|
|
|
|
one = (now() - check_date).days
|
|
|
|
if (now() - check_date).days >= 0 and (now() - check_date).days <= 1:
|
|
|
|
return 'OK'
|
|
|
|
elif (now() - check_date).days >= 2 and (now() - check_date).days < 3:
|
|
|
|
return 'WARN'
|
|
|
|
elif (now() - check_date).days >= 3:
|
|
|
|
return 'BAD'
|
|
|
|
else:
|
|
|
|
return 'UNK'
|
|
|
|
|
|
def get_configuration(self, id):
|
|
|
|
softwares = DeviceSoftware.objects.filter(device=id)
|
|
|
|
config = {
|
|
"software": []
|
|
}
|
|
|
|
host_software = []
|
|
group_software = []
|
|
|
|
for software in softwares:
|
|
|
|
if software.action:
|
|
|
|
if int(software.action) == 1:
|
|
|
|
state = 'present'
|
|
|
|
elif int(software.action) == 0:
|
|
|
|
state = 'absent'
|
|
|
|
software_action = {
|
|
"name": software.software.slug,
|
|
"state": state
|
|
}
|
|
|
|
|
|
if software.version:
|
|
software_action['version'] = software.version.name
|
|
|
|
host_software += [ software_action ]
|
|
|
|
config: dict = config
|
|
|
|
from config_management.models.groups import ConfigGroupHosts
|
|
|
|
if self.id:
|
|
|
|
config_groups = ConfigGroupHosts.objects.filter(host=self.id).order_by('group')
|
|
|
|
for group in config_groups:
|
|
|
|
rendered_config = group.group.render_config()
|
|
|
|
if rendered_config:
|
|
|
|
config.update(json.loads(rendered_config))
|
|
|
|
rendered_config: dict = json.loads(rendered_config)
|
|
|
|
if 'software' in rendered_config.keys():
|
|
|
|
group_software = group_software + rendered_config['software']
|
|
|
|
config['software'] = merge_software(group_software, host_software)
|
|
|
|
if self.config:
|
|
|
|
config.update(self.config)
|
|
|
|
from itim.models.services import Service
|
|
services = Service.objects.filter(
|
|
device = self.pk
|
|
)
|
|
|
|
for service in services:
|
|
|
|
if service.config_variables:
|
|
|
|
service_config:dict = {
|
|
service.config_key_variable: service.config_variables
|
|
}
|
|
|
|
config.update(service_config)
|
|
|
|
return config
|
|
|
|
|
|
class DeviceSoftware(DeviceCommonFields, SaveHistory):
|
|
""" A way for the device owner to configure software to install/remove """
|
|
|
|
class Meta:
|
|
ordering = [
|
|
'-action',
|
|
'software'
|
|
]
|
|
|
|
|
|
class Actions(models.TextChoices):
|
|
INSTALL = '1', 'Install'
|
|
REMOVE = '0', 'Remove'
|
|
|
|
|
|
device = models.ForeignKey(
|
|
Device,
|
|
on_delete=models.CASCADE,
|
|
default = None,
|
|
null = False,
|
|
blank= False
|
|
)
|
|
|
|
software = models.ForeignKey(
|
|
Software,
|
|
on_delete=models.CASCADE,
|
|
default = None,
|
|
null = False,
|
|
blank= False
|
|
)
|
|
|
|
action = models.CharField(
|
|
max_length=1,
|
|
choices=Actions,
|
|
default=None,
|
|
null=True,
|
|
blank = True,
|
|
)
|
|
|
|
version = models.ForeignKey(
|
|
SoftwareVersion,
|
|
on_delete=models.CASCADE,
|
|
default = None,
|
|
null = True,
|
|
blank= True
|
|
)
|
|
|
|
|
|
installedversion = models.ForeignKey(
|
|
SoftwareVersion,
|
|
related_name = 'installedversion',
|
|
on_delete=models.CASCADE,
|
|
default = None,
|
|
null = True,
|
|
blank= True
|
|
)
|
|
|
|
installed = models.DateTimeField(
|
|
verbose_name = 'Install Date',
|
|
null = True,
|
|
blank = True
|
|
)
|
|
|
|
|
|
@property
|
|
def parent_object(self):
|
|
""" Fetch the parent object """
|
|
|
|
return self.device
|
|
|
|
|
|
def save(
|
|
self, force_insert=False, force_update=False, using=None, update_fields=None
|
|
):
|
|
|
|
self.is_global = False
|
|
|
|
super().save(
|
|
force_insert=False, force_update=False, using=None, update_fields=None
|
|
)
|
|
|
|
|
|
class DeviceOperatingSystem(DeviceCommonFields, SaveHistory):
|
|
|
|
device = models.ForeignKey(
|
|
Device,
|
|
on_delete = models.CASCADE,
|
|
default = None,
|
|
null = False,
|
|
blank = False,
|
|
|
|
)
|
|
|
|
operating_system_version = models.ForeignKey(
|
|
OperatingSystemVersion,
|
|
verbose_name = 'Operating System/Version',
|
|
on_delete = models.CASCADE,
|
|
default = None,
|
|
null = False,
|
|
blank = False
|
|
|
|
)
|
|
|
|
version = models.CharField(
|
|
verbose_name = 'Installed Version',
|
|
max_length = 15,
|
|
null = False,
|
|
blank = False,
|
|
)
|
|
|
|
installdate = models.DateTimeField(
|
|
verbose_name = 'Install Date',
|
|
null = True,
|
|
blank = True,
|
|
default = None,
|
|
)
|
|
|
|
|
|
@property
|
|
def parent_object(self):
|
|
""" Fetch the parent object """
|
|
|
|
return self.device
|
|
|
|
|
|
def save(
|
|
self, force_insert=False, force_update=False, using=None, update_fields=None
|
|
):
|
|
|
|
self.is_global = False
|
|
|
|
super().save(
|
|
force_insert=False, force_update=False, using=None, update_fields=None
|
|
)
|