// Copyright Epic Games, Inc. All Rights Reserved. using HordeServer.Api; using HordeServer.Collections; using HordeServer.Models; using HordeServer.Services; using HordeServer.Utilities; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using MongoDB.Bson; using MongoDB.Driver; using System; using System.Collections.Generic; using System.Linq; using System.Text.Json; using System.Threading.Tasks; namespace HordeServer.Controllers { using DeviceId = StringId; using DevicePlatformId = StringId; using DevicePoolId = StringId; using ProjectId = StringId; /// /// Controller for device service /// public class DevicesController : ControllerBase { /// /// The acl service singleton /// AclService AclService; /// /// The user collection instance /// IUserCollection UserCollection { get; set; } /// /// Singleton instance of the device service /// DeviceService DeviceService; /// /// Logger for controller /// private readonly ILogger Logger; /// /// Constructor /// public DevicesController(DeviceService DeviceService, AclService AclService, IUserCollection UserCollection, ILogger Logger) { this.UserCollection = UserCollection; this.DeviceService = DeviceService; this.Logger = Logger; this.AclService = AclService; } // DEVICES /// /// Create a new device /// [HttpPost] [Authorize] [Route("/api/v2/devices")] public async Task> CreateDeviceAsync([FromBody] CreateDeviceRequest DeviceRequest) { DevicePoolAuthorization? PoolAuth = await DeviceService.GetUserPoolAuthorizationAsync(new DevicePoolId(DeviceRequest.PoolId!), User); if (PoolAuth == null || !PoolAuth.Write) { return Forbid(); } IUser? InternalUser = await UserCollection.GetUserAsync(User); if (InternalUser == null) { return NotFound(); } IDevicePlatform? Platform = await DeviceService.GetPlatformAsync(new DevicePlatformId(DeviceRequest.PlatformId!)); if (Platform == null) { return BadRequest($"Bad platform id {DeviceRequest.PlatformId} on request"); } IDevicePool? Pool = await DeviceService.GetPoolAsync(new DevicePoolId(DeviceRequest.PoolId!)); if (Pool == null) { return BadRequest($"Bad pool id {DeviceRequest.PoolId} on request"); } string? ModelId = null; if (!string.IsNullOrEmpty(DeviceRequest.ModelId)) { ModelId = new string(DeviceRequest.ModelId); if (Platform.Models?.FirstOrDefault(x => x == ModelId) == null) { return BadRequest($"Bad model id {ModelId} for platform {Platform.Id} on request"); } } string Name = DeviceRequest.Name.Trim(); string? Address = DeviceRequest.Address?.Trim(); IDevice? Device = await DeviceService.TryCreateDeviceAsync(DeviceId.Sanitize(Name), Name, Platform.Id, Pool.Id, DeviceRequest.Enabled, Address, ModelId, InternalUser.Id); if (Device == null) { return BadRequest($"Unable to create device"); } return new CreateDeviceResponse(Device.Id.ToString()); } /// /// Get list of devices /// [HttpGet] [Authorize] [Route("/api/v2/devices")] [ProducesResponseType(typeof(List), 200)] public async Task>> GetDevicesAsync() { List PoolAuth = await DeviceService.GetUserPoolAuthorizationsAsync(User); List Devices = await DeviceService.GetDevicesAsync(); List Responses = new List(); foreach (IDevice Device in Devices) { DevicePoolAuthorization? Auth = PoolAuth.Where(x => x.Pool.Id == Device.PoolId).FirstOrDefault(); if (Auth == null || !Auth.Read) { continue; } Responses.Add(new GetDeviceResponse(Device.Id.ToString(), Device.PlatformId.ToString(), Device.PoolId.ToString(), Device.Name, Device.Enabled, Device.Address, Device.ModelId?.ToString(), Device.ModifiedByUser, Device.Notes, Device.ProblemTimeUtc, Device.MaintenanceTimeUtc, Device.Utilization, Device.CheckedOutByUser, Device.CheckOutTime)); } return Responses; } /// /// Get a specific device /// [HttpGet] [Authorize] [Route("/api/v2/devices/{DeviceId}")] [ProducesResponseType(typeof(GetDeviceResponse), 200)] public async Task> GetDeviceAsync(string DeviceId) { DeviceId DeviceIdValue = new DeviceId(DeviceId); IDevice? Device = await DeviceService.GetDeviceAsync(DeviceIdValue); if (Device == null) { return BadRequest($"Unable to find device with id {DeviceId}"); } DevicePoolAuthorization? PoolAuth = await DeviceService.GetUserPoolAuthorizationAsync(Device.PoolId, User); if (PoolAuth == null || !PoolAuth.Write) { return Forbid(); } return new GetDeviceResponse(Device.Id.ToString(), Device.PlatformId.ToString(), Device.PoolId.ToString(), Device.Name, Device.Enabled, Device.Address, Device.ModelId?.ToString(), Device.ModifiedByUser?.ToString(), Device.Notes, Device.ProblemTimeUtc, Device.MaintenanceTimeUtc, Device.Utilization, Device.CheckedOutByUser, Device.CheckOutTime); } /// /// Update a specific device /// [HttpPut] [Authorize] [Route("/api/v2/devices/{DeviceId}")] [ProducesResponseType(typeof(List), 200)] public async Task UpdateDeviceAsync(string DeviceId, [FromBody] UpdateDeviceRequest Update) { IUser? InternalUser = await UserCollection.GetUserAsync(User); if (InternalUser == null) { return NotFound(); } DeviceId DeviceIdValue = new DeviceId(DeviceId); IDevice? Device = await DeviceService.GetDeviceAsync(DeviceIdValue); if (Device == null) { return BadRequest($"Device with id ${DeviceId} does not exist"); } DevicePoolAuthorization? PoolAuth = await DeviceService.GetUserPoolAuthorizationAsync(Device.PoolId, User); if (PoolAuth == null || !PoolAuth.Write) { return Forbid(); } IDevicePlatform? Platform = await DeviceService.GetPlatformAsync(Device.PlatformId); if (Platform == null) { return BadRequest($"Platform id ${Device.PlatformId} does not exist"); } DevicePoolId? PoolIdValue = null; if (Update.PoolId != null) { PoolIdValue = new DevicePoolId(Update.PoolId); } string? ModelIdValue = null; if (Update.ModelId != null) { ModelIdValue = new string(Update.ModelId); if (Platform.Models?.FirstOrDefault(x => x == ModelIdValue) == null) { return BadRequest($"Bad model id {Update.ModelId} for platform {Platform.Id} on request"); } } string? Name = Update.Name?.Trim(); string? Address = Update.Address?.Trim(); await DeviceService.UpdateDeviceAsync(DeviceIdValue, PoolIdValue, Name, Address, ModelIdValue, Update.Notes, Update.Enabled, Update.Problem, Update.Maintenance, InternalUser.Id); return Ok(); } /// /// Checkout a device /// [HttpPut] [Authorize] [Route("/api/v2/devices/{DeviceId}/checkout")] [ProducesResponseType(typeof(List), 200)] public async Task CheckoutDeviceAsync(string DeviceId, [FromBody] CheckoutDeviceRequest Request) { IUser? InternalUser = await UserCollection.GetUserAsync(User); if (InternalUser == null) { return NotFound(); } DeviceId DeviceIdValue = new DeviceId(DeviceId); IDevice? Device = await DeviceService.GetDeviceAsync(DeviceIdValue); if (Device == null) { return BadRequest($"Device with id ${DeviceId} does not exist"); } DevicePoolAuthorization? PoolAuth = await DeviceService.GetUserPoolAuthorizationAsync(Device.PoolId, User); if (PoolAuth == null || !PoolAuth.Write) { return Forbid(); } if (Request.Checkout) { if (!string.IsNullOrEmpty(Device.CheckedOutByUser)) { return BadRequest($"Already checked out by user {Device.CheckedOutByUser}"); } await DeviceService.CheckoutDeviceAsync(DeviceIdValue, InternalUser.Id); } else { await DeviceService.CheckoutDeviceAsync(DeviceIdValue, null); } return Ok(); } /// /// Delete a specific device /// [HttpDelete] [Authorize] [Route("/api/v2/devices/{DeviceId}")] public async Task DeleteDeviceAsync(string DeviceId) { if (!await DeviceService.AuthorizeAsync(AclAction.DeviceWrite, User)) { return Forbid(); } DeviceId DeviceIdValue = new DeviceId(DeviceId); IDevice? Device = await DeviceService.GetDeviceAsync(DeviceIdValue); if (Device == null) { return NotFound(); } DevicePoolAuthorization? PoolAuth = await DeviceService.GetUserPoolAuthorizationAsync(Device.PoolId, User); if (PoolAuth == null || !PoolAuth.Write) { return Forbid(); } await DeviceService.DeleteDeviceAsync(DeviceIdValue); return Ok(); } // PLATFORMS /// /// Create a new device platform /// [HttpPost] [Authorize] [Route("/api/v2/devices/platforms")] public async Task> CreatePlatformAsync([FromBody] CreateDevicePlatformRequest Request) { if (!await DeviceService.AuthorizeAsync(AclAction.DeviceWrite, User)) { return Forbid(); } string Name = Request.Name.Trim(); IDevicePlatform? Platform = await DeviceService.TryCreatePlatformAsync(DevicePlatformId.Sanitize(Name), Name); if (Platform == null) { return BadRequest($"Unable to create platform for {Name}"); } return new CreateDevicePlatformResponse(Platform.Id.ToString()); } /// /// Update a device platform /// [HttpPut] [Authorize] [Route("/api/v2/devices/platforms/{PlatformId}")] public async Task> UpdatePlatformAsync(string PlatformId, [FromBody] UpdateDevicePlatformRequest Request) { if (!await DeviceService.AuthorizeAsync(AclAction.DeviceWrite, User)) { return Forbid(); } await DeviceService.UpdatePlatformAsync(new DevicePlatformId(PlatformId), Request.ModelIds); return Ok(); } /// /// Get a list of supported device platforms /// [HttpGet] [Authorize] [Route("/api/v2/devices/platforms")] [ProducesResponseType(typeof(List), 200)] public async Task>> GetDevicePlatformsAsync() { if (!await DeviceService.AuthorizeAsync(AclAction.DeviceRead, User)) { return Forbid(); } List Platforms = await DeviceService.GetPlatformsAsync(); List Responses = new List(); foreach (IDevicePlatform Platform in Platforms) { // @todo: ACL per platform Responses.Add(new GetDevicePlatformResponse(Platform.Id.ToString(), Platform.Name, Platform.Models?.ToArray() ?? Array.Empty())); } return Responses; } // POOLS /// /// Create a new device pool /// [HttpPost] [Authorize] [Route("/api/v2/devices/pools")] public async Task> CreatePoolAsync([FromBody] CreateDevicePoolRequest Request) { if (!await DeviceService.AuthorizeAsync(AclAction.DeviceWrite, User)) { return Forbid(); } string Name = Request.Name.Trim(); IDevicePool? Pool = await DeviceService.TryCreatePoolAsync(DevicePoolId.Sanitize(Name), Name, Request.PoolType, Request.ProjectIds?.Select(x => new ProjectId(x)).ToList()); if (Pool == null) { return BadRequest($"Unable to create pool for {Request.Name}"); } return new CreateDevicePoolResponse(Pool.Id.ToString()); } /// /// Update a device pool /// [HttpPut] [Authorize] [Route("/api/v2/devices/pools")] public async Task UpdatePoolAsync([FromBody] UpdateDevicePoolRequest Request) { DevicePoolAuthorization? PoolAuth = await DeviceService.GetUserPoolAuthorizationAsync(new DevicePoolId(Request.Id), User); if (PoolAuth == null || !PoolAuth.Write) { return Forbid(); } List? ProjectIds = null; if (Request.ProjectIds != null) { ProjectIds = Request.ProjectIds.Select(x => new ProjectId(x)).ToList(); } await DeviceService.UpdatePoolAsync(new DevicePoolId(Request.Id), ProjectIds); return Ok(); } /// /// Get a list of existing device pools /// [HttpGet] [Authorize] [Route("/api/v2/devices/pools")] [ProducesResponseType(typeof(List), 200)] public async Task>> GetDevicePoolsAsync() { List PoolAuth = await DeviceService.GetUserPoolAuthorizationsAsync(User); List Pools = await DeviceService.GetPoolsAsync(); List Responses = new List(); foreach (IDevicePool Pool in Pools) { DevicePoolAuthorization? Auth = PoolAuth.Where(x => x.Pool.Id == Pool.Id).FirstOrDefault(); if (Auth == null || !Auth.Read) { continue; } Responses.Add(new GetDevicePoolResponse(Pool.Id.ToString(), Pool.Name, Pool.PoolType, Auth.Write)); } return Responses; } // RESERVATIONS /// /// Create a new device reservation /// [HttpPost] [Authorize] [Route("/api/v2/devices/reservations")] [ProducesResponseType(typeof(CreateDeviceReservationResponse), 200)] public async Task> CreateDeviceReservation([FromBody] CreateDeviceReservationRequest Request) { if (!await DeviceService.AuthorizeAsync(AclAction.DeviceWrite, User)) { return Forbid(); } List Pools = await DeviceService.GetPoolsAsync(); List Platforms = await DeviceService.GetPlatformsAsync(); DevicePoolId PoolIdValue = new DevicePoolId(Request.PoolId); IDevicePool? Pool = Pools.FirstOrDefault(x => x.Id == PoolIdValue); if (Pool == null) { return BadRequest($"Unknown pool {Request.PoolId} on device reservation request"); } List RequestedDevices = new List(); foreach (DeviceReservationRequest DeviceRequest in Request.Devices) { DevicePlatformId PlatformIdValue = new DevicePlatformId(DeviceRequest.PlatformId); IDevicePlatform? Platform = Platforms.FirstOrDefault(x => x.Id == PlatformIdValue); if (Platform == null) { return BadRequest($"Unknown platform {DeviceRequest.PlatformId} on device reservation request"); } if (DeviceRequest.IncludeModels != null) { foreach (string Model in DeviceRequest.IncludeModels) { if (Platform.Models?.FirstOrDefault(x => x == Model) == null) { return BadRequest($"Unknown model {Model} for platform {DeviceRequest.PlatformId} on device reservation request"); } } } if (DeviceRequest.ExcludeModels != null) { foreach (string Model in DeviceRequest.ExcludeModels) { if (Platform.Models?.FirstOrDefault(x => x == Model) == null) { return BadRequest($"Unknown model {Model} for platform {DeviceRequest.PlatformId} on device reservation request"); } } } RequestedDevices.Add(new DeviceRequestData(PlatformIdValue, DeviceRequest.IncludeModels, DeviceRequest.ExcludeModels)); } IDeviceReservation? Reservation = await DeviceService.TryCreateReservationAsync(PoolIdValue, RequestedDevices); if (Reservation == null) { return Conflict("Unable to allocated devices for reservation"); } List Devices = await DeviceService.GetDevicesAsync(Reservation.Devices); CreateDeviceReservationResponse Response = new CreateDeviceReservationResponse(); Response.Id = Reservation.Id.ToString(); foreach (IDevice Device in Devices) { Response.Devices.Add(new GetDeviceResponse(Device.Id.ToString(), Device.PlatformId.ToString(), Device.PoolId.ToString(), Device.Name, Device.Enabled, Device.Address, Device.ModelId?.ToString(), Device.ModifiedByUser, Device.Notes, Device.ProblemTimeUtc, Device.MaintenanceTimeUtc, Device.Utilization)); } return Response; } /// /// Get active device reservations /// [HttpGet] [Authorize] [Route("/api/v2/devices/reservations")] [ProducesResponseType(typeof(List), 200)] public async Task>> GetDeviceReservations() { if (!await DeviceService.AuthorizeAsync(AclAction.DeviceRead, User)) { return Forbid(); } List Reservations = await DeviceService.GetReservationsAsync(); List Response = new List(); foreach (IDeviceReservation Reservation in Reservations) { GetDeviceReservationResponse ReservationResponse = new GetDeviceReservationResponse() { Id = Reservation.Id.ToString(), PoolId = Reservation.PoolId.ToString(), Devices = Reservation.Devices.Select(x => x.ToString()).ToList(), JobId = Reservation.JobId?.ToString(), StepId = Reservation.StepId?.ToString(), UserId = Reservation.UserId?.ToString(), Hostname = Reservation.Hostname, ReservationDetails = Reservation.ReservationDetails, CreateTimeUtc = Reservation.CreateTimeUtc, LegacyGuid = Reservation.LegacyGuid }; Response.Add(ReservationResponse); } return Response; } /// /// Renew an existing reservation /// [HttpPut] [Authorize] [Route("/api/v2/devices/reservations/{ReservationId}")] public async Task UpdateReservationAsync(string ReservationId) { if (!await DeviceService.AuthorizeAsync(AclAction.DeviceWrite, User)) { return Forbid(); } ObjectId ReservationIdValue = new ObjectId(ReservationId); bool Updated = await DeviceService.TryUpdateReservationAsync(ReservationIdValue); if (!Updated) { return BadRequest("Failed to update reservation"); } return Ok(); } /// /// Delete a reservation /// [HttpDelete] [Authorize] [Route("/api/v2/devices/reservations/{ReservationId}")] public async Task DeleteReservationAsync(string ReservationId) { if (!await DeviceService.AuthorizeAsync(AclAction.DeviceWrite, User)) { return Forbid(); } ObjectId ReservationIdValue = new ObjectId(ReservationId); bool Deleted = await DeviceService.DeleteReservationAsync(ReservationIdValue); if (!Deleted) { return BadRequest("Failed to delete reservation"); } return Ok(); } // LEGACY V1 API enum LegacyPerfSpec { Unspecified, Minimum, Recommended, High }; /// /// Create a device reservation /// [HttpPost] [Route("/api/v1/reservations")] [ProducesResponseType(typeof(GetLegacyReservationResponse), 200)] public async Task> CreateDeviceReservationV1Async([FromBody] LegacyCreateReservationRequest Request) { List Pools = await DeviceService.GetPoolsAsync(); List Platforms = await DeviceService.GetPlatformsAsync(); string Message; string? PoolId = Request.PoolId; // @todo: Remove this once all streams are updated to provide jobid string Details = ""; if ((String.IsNullOrEmpty(Request.JobId) || String.IsNullOrEmpty(Request.StepId))) { if (!string.IsNullOrEmpty(Request.ReservationDetails)) { Details = $" - {Request.ReservationDetails}"; } else { Details = $" - Host {Request.Hostname}"; } } if (string.IsNullOrEmpty(PoolId)) { Message = $"No pool specified, defaulting to UE4" + Details; Logger.LogError(Message + $" JobId: {Request.JobId}, StepId: {Request.StepId}"); await DeviceService.NotifyDeviceServiceAsync(Message, null, Request.JobId, Request.StepId); PoolId = "ue4"; //return BadRequest(Message); } DevicePoolId PoolIdValue = DevicePoolId.Sanitize(PoolId); IDevicePool? Pool = Pools.FirstOrDefault(x => x.Id == PoolIdValue); if (Pool == null) { Message = $"Unknown pool {PoolId} " + Details; Logger.LogError(Message); await DeviceService.NotifyDeviceServiceAsync(Message, null, Request.JobId, Request.StepId); return BadRequest(Message); } List RequestedDevices = new List(); List PerfSpecs = new List(); foreach (string DeviceType in Request.DeviceTypes) { string PlatformName = DeviceType; string PerfSpecName = "Unspecified"; if (DeviceType.Contains(":", StringComparison.OrdinalIgnoreCase)) { string[] Tokens = DeviceType.Split(":"); PlatformName = Tokens[0]; PerfSpecName = Tokens[1]; } PerfSpecs.Add(PerfSpecName); DevicePlatformId PlatformId; DevicePlatformMapV1 MapV1 = await DeviceService.GetPlatformMapV1(); if (!MapV1.PlatformMap.TryGetValue(PlatformName, out PlatformId)) { Message = $"Unknown platform {PlatformName}" + Details; Logger.LogError(Message); await DeviceService.NotifyDeviceServiceAsync(Message, null, Request.JobId, Request.StepId); return BadRequest(Message); } IDevicePlatform? Platform = Platforms.FirstOrDefault(x => x.Id == PlatformId); if (Platform == null) { Message = $"Unknown platform {PlatformId}" + Details; Logger.LogError(Message); await DeviceService.NotifyDeviceServiceAsync(Message, null, Request.JobId, Request.StepId); return BadRequest(Message); } List IncludeModels = new List(); List ExcludeModels = new List(); if (PerfSpecName == "High") { string? Model = null; if (MapV1.PerfSpecHighMap.TryGetValue(PlatformId, out Model)) { IncludeModels.Add(Model); } } if (PerfSpecName == "Minimum" || PerfSpecName == "Recommended") { string? Model = null; if (MapV1.PerfSpecHighMap.TryGetValue(PlatformId, out Model)) { ExcludeModels.Add(Model); } } RequestedDevices.Add(new DeviceRequestData(PlatformId, IncludeModels, ExcludeModels)); } IDeviceReservation? Reservation = await DeviceService.TryCreateReservationAsync(PoolIdValue, RequestedDevices, Request.Hostname, Request.ReservationDetails, Request.JobId, Request.StepId); if (Reservation == null) { return Conflict("Unable to allocated devices for reservation"); } List Devices = await DeviceService.GetDevicesAsync(Reservation.Devices); GetLegacyReservationResponse Response = new GetLegacyReservationResponse(); Response.Guid = Reservation.LegacyGuid; Response.DeviceNames = Devices.Select(x => x.Name).ToArray(); Response.DevicePerfSpecs = PerfSpecs.ToArray(); Response.HostName = Request.Hostname; Response.StartDateTime = Reservation.CreateTimeUtc.ToString("O", System.Globalization.CultureInfo.InvariantCulture); Response.Duration = $"{Request.Duration}"; return new JsonResult(Response, new JsonSerializerOptions() { PropertyNamingPolicy = null }); } /// /// Renew a reservation /// [HttpPut] [Route("/api/v1/reservations/{ReservationGuid}")] [ProducesResponseType(typeof(GetLegacyReservationResponse), 200)] public async Task> UpdateReservationV1Async(string ReservationGuid /* [FromBody] string Duration */) { IDeviceReservation? Reservation = await DeviceService.TryGetReservationFromLegacyGuidAsync(ReservationGuid); if (Reservation == null) { Logger.LogError($"Unable to find reservation for legacy guid {ReservationGuid}"); return BadRequest(); } bool Updated = await DeviceService.TryUpdateReservationAsync(Reservation.Id); if (!Updated) { Logger.LogError($"Unable to find reservation for reservation {Reservation.Id}"); return BadRequest(); } List Devices = await DeviceService.GetDevicesAsync(Reservation.Devices); GetLegacyReservationResponse Response = new GetLegacyReservationResponse(); Response.Guid = Reservation.LegacyGuid; Response.DeviceNames = Reservation.Devices.Select(DeviceId => Devices.First(D => D.Id == DeviceId).Name).ToArray(); Response.HostName = Reservation.Hostname ?? ""; Response.StartDateTime = Reservation.CreateTimeUtc.ToString("O", System.Globalization.CultureInfo.InvariantCulture); Response.Duration = "00:10:00"; // matches gauntlet duration return new JsonResult(Response, new JsonSerializerOptions() { PropertyNamingPolicy = null }); } /// /// Delete a reservation /// [HttpDelete] [Route("/api/v1/reservations/{ReservationGuid}")] public async Task DeleteReservationV1Async(string ReservationGuid) { IDeviceReservation? Reservation = await DeviceService.TryGetReservationFromLegacyGuidAsync(ReservationGuid); if (Reservation == null) { return BadRequest($"Unable to find reservation for guid {ReservationGuid}"); } bool Deleted = await DeviceService.DeleteReservationAsync(Reservation.Id); if (!Deleted) { return BadRequest("Failed to delete reservation"); } return Ok(); } /// /// Get device info for a reserved device /// [HttpGet] [Route("/api/v1/devices/{DeviceName}")] [ProducesResponseType(typeof(GetLegacyDeviceResponse), 200)] public async Task> GetDeviceV1Async(string DeviceName) { IDevice? Device = await DeviceService.GetDeviceByNameAsync(DeviceName); if (Device == null) { return BadRequest($"Unknown device {DeviceName}"); } DevicePlatformMapV1 MapV1 = await DeviceService.GetPlatformMapV1(); string? Platform; if (!MapV1.PlatformReverseMap.TryGetValue(Device.PlatformId, out Platform)) { return BadRequest($"Unable to map platform for {DeviceName} : {Device.PlatformId}"); } GetLegacyDeviceResponse Response = new GetLegacyDeviceResponse(); Response.Name = Device.Name; Response.Type = Platform; Response.IPOrHostName = Device.Address ?? ""; Response.AvailableStartTime = "00:00:00"; Response.AvailableEndTime = "00:00:00"; Response.Enabled = true; Response.DeviceData = ""; Response.PerfSpec = "Minimum"; if (Device.ModelId != null) { string? Model = null; if (MapV1.PerfSpecHighMap.TryGetValue(Device.PlatformId, out Model)) { if (Model == Device.ModelId) { Response.PerfSpec = "High"; } } } return new JsonResult(Response, new JsonSerializerOptions() { PropertyNamingPolicy = null }); } /// /// Mark a problem device /// [HttpPut] [Route("/api/v1/deviceerror/{DeviceName}")] public async Task PutDeviceErrorAsync(string DeviceName) { IDevice? Device = await DeviceService.GetDeviceByNameAsync(DeviceName); if (Device == null) { Logger.LogError($"Device error reported for unknown device {DeviceName}"); return BadRequest($"Unknown device {DeviceName}"); } await DeviceService.UpdateDeviceAsync(Device.Id, NewProblem: true); string Message = $"Device problem, {Device.Name} : {Device.PoolId.ToString().ToUpperInvariant()}"; IDeviceReservation? Reservation = await DeviceService.TryGetDeviceReservation(Device.Id); string? JobId = null; string? StepId = null; if (Reservation != null) { JobId = !string.IsNullOrEmpty(Reservation.JobId) ? Reservation.JobId : null; StepId = !string.IsNullOrEmpty(Reservation.StepId) ? Reservation.StepId : null; if ((JobId == null || StepId == null)) { if (!string.IsNullOrEmpty(Reservation.ReservationDetails)) { Message += $" - {Reservation.ReservationDetails}"; } else { Message += $" - Host {Reservation.Hostname}"; } } } Logger.LogError(Message); await DeviceService.NotifyDeviceServiceAsync(Message, Device.Id, JobId, StepId); return Ok(); } /// /// Updates the platform map for v1 requests /// [HttpPut] [Route("/api/v1/devices/platformmap")] public async Task UpdatePlatformMapV1([FromBody] UpdatePlatformMapRequest Request) { if (!await AclService.AuthorizeAsync(AclAction.AdminWrite, User)) { return Forbid(); } bool Result = false; try { Result = await DeviceService.UpdatePlatformMapAsync(Request); } catch (Exception Ex) { Logger.LogError(Ex, "Error updating device platform map {Message}", Ex.Message); throw; } if (!Result) { Logger.LogError("Unable to update device platform mapping"); return BadRequest(); } return Ok(); } } }