Files
centurion_erp/app/itam/viewsets/inventory.py

226 lines
6.1 KiB
Python

from django.db.models import Q
from kombu.exceptions import OperationalError
from drf_spectacular.utils import extend_schema, extend_schema_view, OpenApiResponse
from rest_framework.response import Response
from api.viewsets.common import ModelCreateViewSet
from core import exceptions as centurion_exception
from core.http.common import Http
from itam.models.device import Device
from itam.serializers.inventory import InventorySerializer
from itam.tasks.inventory import process_inventory
from settings.models.user_settings import UserSettings
@extend_schema_view(
create=extend_schema(
summary = "Upload a device's inventory",
description = """After inventorying a device, it's inventory file, `.json` is uploaded to this endpoint.
If the device does not exist, it will be created. If the device does exist the existing
device will be updated with the information within the inventory.
matching for an existing device is by slug which is the hostname converted to lower case
letters. This conversion is automagic.
**NOTE:** _for device creation, the API user must have user setting 'Default Organization'. Without
this setting populated, no device will be created and the endpoint will return HTTP/403_
## Permissions
- `itam.add_device` Required to upload inventory
""",
request = InventorySerializer,
responses = {
200: OpenApiResponse(
description='Inventory upload successful',
response = {
'OK'
}
),
400: OpenApiResponse(description='Error Occured, see output retured'),
401: OpenApiResponse(description='User Not logged in'),
403: OpenApiResponse(description='User is missing permission or in different organization'),
500: OpenApiResponse(description='Exception occured. View server logs for the Stack Trace'),
}
)
)
class ViewSet( ModelCreateViewSet ):
"""Device Inventory
Use this endpoint to upload your device inventories.
"""
model = Device
serializer_class = InventorySerializer
view_name = 'Device Inventory'
view_description = __doc__
inventory_action: str = None
"""Inventory action, choice. new|update"""
def create(self, request, *args, **kwargs):
"""Upload a device inventory
Raises:
centurion_exceptions.PermissionDenied: User is missing the required permissions
Returns:
Response: string denoting what has occured
"""
status = Http.Status.OK
response_data = 'OK'
try:
data = InventorySerializer(
data = request.data
)
device = None
if not data.is_valid():
raise centurion_exception.ValidationError(
detail = 'Uploaded inventory is not valid',
code = 'invalid_inventory'
)
self.default_organization = UserSettings.objects.get(user=request.user).default_organization
obj_organaization_id = getattr(self.default_organization, 'id', None)
obj = Device.objects.filter(
Q(
name=str(data.validated_data['details']['name']).lower(),
serial_number = str(data.validated_data['details']['serial_number']).lower()
)
|
Q(
name = str(data.validated_data['details']['name']).lower(),
uuid = str(data.validated_data['details']['uuid']).lower()
)
)
if len(obj) == 1:
obj_organaization_id = obj[0].organization.id
if not obj_organaization_id:
raise centurion_exception.ValidationError({
'detail': 'No Default organization set for user'
})
task = process_inventory.delay(data.validated_data, obj_organaization_id)
response_data: dict = {"task_id": f"{task.id}"}
except OperationalError as e:
status = 503
response_data = f'RabbitMQ error: {e.args[0]}'
except centurion_exception.PermissionDenied as e:
status = Http.Status.FORBIDDEN
response_data = e.detail
except centurion_exception.ValidationError as e:
status = Http.Status.BAD_REQUEST
response_data = e.detail
except Exception as e:
print(f'An error occured{e}')
status = Http.Status.SERVER_ERROR
response_data = f'Unknown Server Error occured: {e}'
return Response(data=response_data,status=status)
def get_dynamic_permissions(self):
"""Obtain the permissions required to upload an inventory.
Returns:
list: Permissions required for Inventory Upload
"""
organization = None
device_search = None
if 'details' in self.request.data:
if 'name' in self.request.data['details']:
device_search = Device.objects.filter(
slug = str(self.request.data['details']['name']).lower()
)
else:
centurion_exception.ParseError(
detail = {
'name': 'Device name is required'
},
code = 'missing_device_name'
)
else:
centurion_exception.ParseError(
detail = {
'details': 'Details dict is required'
},
code = 'missing_details_dict'
)
if device_search: # Existing device
if len(list(device_search)) == 1:
self.obj = list(device_search)[0]
self.permission_required = [
'itam.change_device'
]
self.inventory_action = 'update'
else: # New device
self.permission_required = [
'itam.add_device'
]
self.inventory_action = 'new'
return self.permission_required
def get_serializer_class(self):
return InventorySerializer