226 lines
6.1 KiB
Python
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
|