From ce0645e534fee0525551054088104e03968bf1ce Mon Sep 17 00:00:00 2001 From: ipatini Date: Fri, 6 Oct 2023 15:02:23 +0300 Subject: [PATCH] RD: Implemented Device management [WIP] --- management/pom.xml | 5 + .../discovery/ResourceDiscoveryConfig.java | 19 +++ .../resource/discovery/SecurityConfig.java | 2 - .../discovery/monitor/DeviceProcessor.java | 14 ++ .../ArchivedDeviceManagementController.java | 50 ++++++ .../DeviceManagementController.java | 72 ++++++++ .../monitor/model/ArchivedDevice.java | 9 + .../discovery/monitor/model/Device.java | 41 +++++ .../monitor/model/DeviceException.java | 8 + .../discovery/monitor/model/DeviceStatus.java | 8 + .../repository/ArchivedDeviceRepository.java | 14 ++ .../monitor/repository/DeviceRepository.java | 12 ++ .../service/DeviceConversionService.java | 26 +++ .../service/DeviceManagementService.java | 154 ++++++++++++++++++ 14 files changed, 432 insertions(+), 2 deletions(-) create mode 100644 management/src/main/java/eu/nebulous/resource/discovery/ResourceDiscoveryConfig.java create mode 100644 management/src/main/java/eu/nebulous/resource/discovery/monitor/DeviceProcessor.java create mode 100644 management/src/main/java/eu/nebulous/resource/discovery/monitor/controller/ArchivedDeviceManagementController.java create mode 100644 management/src/main/java/eu/nebulous/resource/discovery/monitor/controller/DeviceManagementController.java create mode 100644 management/src/main/java/eu/nebulous/resource/discovery/monitor/model/ArchivedDevice.java create mode 100644 management/src/main/java/eu/nebulous/resource/discovery/monitor/model/Device.java create mode 100644 management/src/main/java/eu/nebulous/resource/discovery/monitor/model/DeviceException.java create mode 100644 management/src/main/java/eu/nebulous/resource/discovery/monitor/model/DeviceStatus.java create mode 100644 management/src/main/java/eu/nebulous/resource/discovery/monitor/repository/ArchivedDeviceRepository.java create mode 100644 management/src/main/java/eu/nebulous/resource/discovery/monitor/repository/DeviceRepository.java create mode 100644 management/src/main/java/eu/nebulous/resource/discovery/monitor/service/DeviceConversionService.java create mode 100644 management/src/main/java/eu/nebulous/resource/discovery/monitor/service/DeviceManagementService.java diff --git a/management/pom.xml b/management/pom.xml index 8499457..e055c31 100644 --- a/management/pom.xml +++ b/management/pom.xml @@ -66,6 +66,11 @@ org.springframework.boot spring-boot-starter-data-mongodb + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + diff --git a/management/src/main/java/eu/nebulous/resource/discovery/ResourceDiscoveryConfig.java b/management/src/main/java/eu/nebulous/resource/discovery/ResourceDiscoveryConfig.java new file mode 100644 index 0000000..dbdb7e9 --- /dev/null +++ b/management/src/main/java/eu/nebulous/resource/discovery/ResourceDiscoveryConfig.java @@ -0,0 +1,19 @@ +package eu.nebulous.resource.discovery; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Slf4j +@Configuration +public class ResourceDiscoveryConfig { + @Bean + public static ObjectMapper objectMapper() { + return new ObjectMapper() + .registerModule(new JavaTimeModule()) + .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); + } +} diff --git a/management/src/main/java/eu/nebulous/resource/discovery/SecurityConfig.java b/management/src/main/java/eu/nebulous/resource/discovery/SecurityConfig.java index 9449c39..07b2216 100644 --- a/management/src/main/java/eu/nebulous/resource/discovery/SecurityConfig.java +++ b/management/src/main/java/eu/nebulous/resource/discovery/SecurityConfig.java @@ -1,6 +1,5 @@ package eu.nebulous.resource.discovery; -import lombok.Data; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Bean; @@ -16,7 +15,6 @@ import org.springframework.security.provisioning.InMemoryUserDetailsManager; import org.springframework.security.web.SecurityFilterChain; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import static org.springframework.security.config.Customizer.withDefaults; diff --git a/management/src/main/java/eu/nebulous/resource/discovery/monitor/DeviceProcessor.java b/management/src/main/java/eu/nebulous/resource/discovery/monitor/DeviceProcessor.java new file mode 100644 index 0000000..3a97f7d --- /dev/null +++ b/management/src/main/java/eu/nebulous/resource/discovery/monitor/DeviceProcessor.java @@ -0,0 +1,14 @@ + +package eu.nebulous.resource.discovery.monitor; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +@Slf4j +@Service +//@EnableAsync +//@EnableScheduling +@RequiredArgsConstructor +public class DeviceProcessor { +} diff --git a/management/src/main/java/eu/nebulous/resource/discovery/monitor/controller/ArchivedDeviceManagementController.java b/management/src/main/java/eu/nebulous/resource/discovery/monitor/controller/ArchivedDeviceManagementController.java new file mode 100644 index 0000000..5b2724d --- /dev/null +++ b/management/src/main/java/eu/nebulous/resource/discovery/monitor/controller/ArchivedDeviceManagementController.java @@ -0,0 +1,50 @@ +package eu.nebulous.resource.discovery.monitor.controller; + +import eu.nebulous.resource.discovery.monitor.model.ArchivedDevice; +import eu.nebulous.resource.discovery.monitor.model.Device; +import eu.nebulous.resource.discovery.monitor.model.DeviceException; +import eu.nebulous.resource.discovery.monitor.service.DeviceManagementService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.http.MediaType; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@Slf4j +@RestController +@RequiredArgsConstructor +@RequestMapping("/monitor/archived") +@PreAuthorize("hasAuthority('ROLE_ADMIN')") +public class ArchivedDeviceManagementController { + private final DeviceManagementService deviceService; + + @GetMapping(value = "/device/all", produces = MediaType.APPLICATION_JSON_VALUE) + public List listDevicesAll() { + return deviceService.getArchivedAll(); + } + + @GetMapping(value = "/device/owner/{owner}", produces = MediaType.APPLICATION_JSON_VALUE) + public List listDevicesForOwner(@PathVariable String owner) { + return deviceService.getArchivedByOwner(owner); + } + + @GetMapping(value = "/device/{id}", produces = MediaType.APPLICATION_JSON_VALUE) + public Device getDevice(@PathVariable String id) { + return deviceService.getArchivedById(id) + .orElseThrow(() -> new DeviceException("Not found archived device with id: "+id)); + } + + @GetMapping(value = "/device/ipaddress/{ipAddress}", produces = MediaType.APPLICATION_JSON_VALUE) + public List getDeviceByIpAddress(@PathVariable String ipAddress) { + return deviceService.getArchivedByIpAddress(ipAddress); + } + + @GetMapping(value = "/device/{id}/unarchive", produces = MediaType.APPLICATION_JSON_VALUE) + public String unarchiveDevice(@PathVariable String id) { + deviceService.unarchiveDevice(id); + return "UNARCHIVED"; + } +} diff --git a/management/src/main/java/eu/nebulous/resource/discovery/monitor/controller/DeviceManagementController.java b/management/src/main/java/eu/nebulous/resource/discovery/monitor/controller/DeviceManagementController.java new file mode 100644 index 0000000..fa06898 --- /dev/null +++ b/management/src/main/java/eu/nebulous/resource/discovery/monitor/controller/DeviceManagementController.java @@ -0,0 +1,72 @@ +package eu.nebulous.resource.discovery.monitor.controller; + +import eu.nebulous.resource.discovery.monitor.model.Device; +import eu.nebulous.resource.discovery.monitor.model.DeviceException; +import eu.nebulous.resource.discovery.monitor.service.DeviceConversionService; +import eu.nebulous.resource.discovery.monitor.service.DeviceManagementService; +import eu.nebulous.resource.discovery.registration.IRegistrationRequestProcessor; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.http.MediaType; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@Slf4j +@RestController +@RequiredArgsConstructor +@RequestMapping("/monitor") +@PreAuthorize("hasAuthority('ROLE_ADMIN')") +public class DeviceManagementController { + private final DeviceManagementService deviceService; + private final DeviceConversionService deviceConversionService; + private final IRegistrationRequestProcessor deviceRequestProcessor; + + @GetMapping(value = "/device/all", produces = MediaType.APPLICATION_JSON_VALUE) + public List listDevicesAll() { + return deviceService.getAll(); + } + + @GetMapping(value = "/device/owner/{owner}", produces = MediaType.APPLICATION_JSON_VALUE) + public List listDevicesForOwner(@PathVariable String owner) { + return deviceService.getByOwner(owner); + } + + @GetMapping(value = "/device/{id}", produces = MediaType.APPLICATION_JSON_VALUE) + public Device getDevice(@PathVariable String id) { + return deviceService.getById(id) + .orElseThrow(() -> new DeviceException("Not found device with id: "+id)); + } + + @GetMapping(value = "/device/ipaddress/{ipAddress}", produces = MediaType.APPLICATION_JSON_VALUE) + public Device getDeviceByIpAddress(@PathVariable String ipAddress) { + return deviceService.getByIpAddress(ipAddress) + .orElseThrow(() -> new DeviceException("Not found device with IP address: "+ipAddress)); + } + + @PutMapping(value = "/device", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) + public Device createDevice(@RequestBody Device device) { + return deviceService.save(device); + } + + @PostMapping(value = "/device/{id}", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) + public Device updateDevice(@PathVariable String id, @RequestBody Device device) { + if (! StringUtils.equals(id, device.getId())) + throw new DeviceException( + "Id does not match the id in device: "+id+" <> "+device.getId()); + return deviceService.update(device); + } + + @DeleteMapping(value = "/device/{id}") + public void deleteDevice(@PathVariable String id) { + deviceService.deleteById(id); + } + + @GetMapping(value = "/device/{id}/archive", produces = MediaType.APPLICATION_JSON_VALUE) + public String archiveDevice(@PathVariable String id) { + deviceService.archiveDevice(id); + return "ARCHIVED"; + } +} diff --git a/management/src/main/java/eu/nebulous/resource/discovery/monitor/model/ArchivedDevice.java b/management/src/main/java/eu/nebulous/resource/discovery/monitor/model/ArchivedDevice.java new file mode 100644 index 0000000..c91c84d --- /dev/null +++ b/management/src/main/java/eu/nebulous/resource/discovery/monitor/model/ArchivedDevice.java @@ -0,0 +1,9 @@ +package eu.nebulous.resource.discovery.monitor.model; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.mongodb.core.mapping.Document; + +@Slf4j +@Document(collection = "archived_device") +public class ArchivedDevice extends Device { +} diff --git a/management/src/main/java/eu/nebulous/resource/discovery/monitor/model/Device.java b/management/src/main/java/eu/nebulous/resource/discovery/monitor/model/Device.java new file mode 100644 index 0000000..187fad4 --- /dev/null +++ b/management/src/main/java/eu/nebulous/resource/discovery/monitor/model/Device.java @@ -0,0 +1,41 @@ +package eu.nebulous.resource.discovery.monitor.model; + +import eu.nebulous.resource.discovery.registration.model.RegistrationRequest; +import lombok.AccessLevel; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.experimental.SuperBuilder; +import org.springframework.data.mongodb.core.mapping.Document; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +@Data +@SuperBuilder +@NoArgsConstructor +@Document(collection = "device") +public class Device { + private String id; + private String os; + private String name; + private String owner; + private String ipAddress; + private String username; + private char[] password; + private char[] publicKey; + private Map deviceInfo; + + private RegistrationRequest request; + private String requestId; + private Instant creationDate; + private Instant lastUpdateDate; + private Instant archiveDate; + private DeviceStatus status; + + private String nodeReference; + @Setter(AccessLevel.NONE) + private List messages = new ArrayList<>(); +} diff --git a/management/src/main/java/eu/nebulous/resource/discovery/monitor/model/DeviceException.java b/management/src/main/java/eu/nebulous/resource/discovery/monitor/model/DeviceException.java new file mode 100644 index 0000000..9db438d --- /dev/null +++ b/management/src/main/java/eu/nebulous/resource/discovery/monitor/model/DeviceException.java @@ -0,0 +1,8 @@ + +package eu.nebulous.resource.discovery.monitor.model; + +public class DeviceException extends RuntimeException { + public DeviceException(String message) { super(message); } + public DeviceException(Throwable t) { super(t); } + public DeviceException(String message, Throwable t) { super(message, t); } +} diff --git a/management/src/main/java/eu/nebulous/resource/discovery/monitor/model/DeviceStatus.java b/management/src/main/java/eu/nebulous/resource/discovery/monitor/model/DeviceStatus.java new file mode 100644 index 0000000..1b45cf7 --- /dev/null +++ b/management/src/main/java/eu/nebulous/resource/discovery/monitor/model/DeviceStatus.java @@ -0,0 +1,8 @@ +package eu.nebulous.resource.discovery.monitor.model; + +public enum DeviceStatus { + NEW_DEVICE, ON_HOLD, + ONBOARDING, ONBOARDED, ONBOARD_ERROR, + HEALTHY, BUSY, IDLE, + OFFBOARDING, OFFBOARDED, OFFBOARD_ERROR +} diff --git a/management/src/main/java/eu/nebulous/resource/discovery/monitor/repository/ArchivedDeviceRepository.java b/management/src/main/java/eu/nebulous/resource/discovery/monitor/repository/ArchivedDeviceRepository.java new file mode 100644 index 0000000..001b89e --- /dev/null +++ b/management/src/main/java/eu/nebulous/resource/discovery/monitor/repository/ArchivedDeviceRepository.java @@ -0,0 +1,14 @@ +package eu.nebulous.resource.discovery.monitor.repository; + +import eu.nebulous.resource.discovery.monitor.model.ArchivedDevice; +import eu.nebulous.resource.discovery.monitor.model.Device; +import eu.nebulous.resource.discovery.registration.model.RegistrationRequest; +import org.springframework.data.mongodb.repository.MongoRepository; + +import java.util.List; +import java.util.Optional; + +public interface ArchivedDeviceRepository extends MongoRepository { + List findByOwner(String owner); + List findByIpAddress(String ipAddress); +} diff --git a/management/src/main/java/eu/nebulous/resource/discovery/monitor/repository/DeviceRepository.java b/management/src/main/java/eu/nebulous/resource/discovery/monitor/repository/DeviceRepository.java new file mode 100644 index 0000000..da30f95 --- /dev/null +++ b/management/src/main/java/eu/nebulous/resource/discovery/monitor/repository/DeviceRepository.java @@ -0,0 +1,12 @@ +package eu.nebulous.resource.discovery.monitor.repository; + +import eu.nebulous.resource.discovery.monitor.model.Device; +import org.springframework.data.mongodb.repository.MongoRepository; + +import java.util.List; +import java.util.Optional; + +public interface DeviceRepository extends MongoRepository { + List findByOwner(String owner); + Optional findByIpAddress(String ipAddress); +} diff --git a/management/src/main/java/eu/nebulous/resource/discovery/monitor/service/DeviceConversionService.java b/management/src/main/java/eu/nebulous/resource/discovery/monitor/service/DeviceConversionService.java new file mode 100644 index 0000000..43532e2 --- /dev/null +++ b/management/src/main/java/eu/nebulous/resource/discovery/monitor/service/DeviceConversionService.java @@ -0,0 +1,26 @@ +package eu.nebulous.resource.discovery.monitor.service; + +import com.fasterxml.jackson.databind.ObjectMapper; +import eu.nebulous.resource.discovery.monitor.model.ArchivedDevice; +import eu.nebulous.resource.discovery.monitor.model.Device; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +@Slf4j +@Service +@RequiredArgsConstructor +public class DeviceConversionService { + private final ObjectMapper objectMapper; + + public ArchivedDevice toArchivedDevice(@NonNull Device device) { + return objectMapper + .convertValue(device, ArchivedDevice.class); + } + + public Device toDevice(@NonNull ArchivedDevice archivedDevice) { + return objectMapper + .convertValue(archivedDevice, Device.class); + } +} diff --git a/management/src/main/java/eu/nebulous/resource/discovery/monitor/service/DeviceManagementService.java b/management/src/main/java/eu/nebulous/resource/discovery/monitor/service/DeviceManagementService.java new file mode 100644 index 0000000..3955c3f --- /dev/null +++ b/management/src/main/java/eu/nebulous/resource/discovery/monitor/service/DeviceManagementService.java @@ -0,0 +1,154 @@ +package eu.nebulous.resource.discovery.monitor.service; + +import eu.nebulous.resource.discovery.monitor.model.ArchivedDevice; +import eu.nebulous.resource.discovery.monitor.model.Device; +import eu.nebulous.resource.discovery.monitor.model.DeviceException; +import eu.nebulous.resource.discovery.monitor.model.DeviceStatus; +import eu.nebulous.resource.discovery.monitor.repository.ArchivedDeviceRepository; +import eu.nebulous.resource.discovery.monitor.repository.DeviceRepository; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +import java.time.Instant; +import java.util.*; + +@Slf4j +@Service +@RequiredArgsConstructor +public class DeviceManagementService { + private final DeviceRepository deviceRepository; + private final ArchivedDeviceRepository archivedDeviceRepository; + private final DeviceConversionService deviceConversionService; + + // ------------------------------------------------------------------------ + + public List getAll() { + return Collections.unmodifiableList(deviceRepository.findAll()); + } + + public List getByOwner(@NonNull String owner) { + return deviceRepository.findByOwner(owner); + } + + public Optional getById(@NonNull String id) { + return deviceRepository.findById(id); + } + + public Optional getByIpAddress(@NonNull String ipAddress) { + return deviceRepository.findByIpAddress(ipAddress); + } + + public @NonNull Device save(@NonNull Device device) { + DeviceStatus status = device.getStatus(); + if (status == null) { + device.setStatus(DeviceStatus.NEW_DEVICE); + } + if (status != null && status != DeviceStatus.NEW_DEVICE) { + throw new DeviceException("Cannot save a new device with status "+status); + } + if (StringUtils.isBlank(device.getId())) { + device.setId( UUID.randomUUID().toString() ); + } else { + throw new DeviceException( + "New device already has an Id: " + device.getId()); + } + if (getById(device.getOs()).isPresent()) + throw new DeviceException( + "A device with the same Id already exists in repository: "+device.getId()); + if (getByIpAddress(device.getIpAddress()).isPresent()) + throw new DeviceException( + "A device with the same IP address already exists in repository: "+device.getIpAddress()); + device.setCreationDate(Instant.now()); + checkDevice(device); + + deviceRepository.save(device); + return device; + } + + public Device update(@NonNull Device device) { + Optional result = getById(device.getId()); + if (result.isEmpty()) + throw new DeviceException( + "Device with the Id does not exists in repository: "+device.getId()); + checkDevice(device); + + device.setLastUpdateDate(Instant.now()); + deviceRepository.save(device); + + return getById(device.getId()).orElseThrow(() -> + new DeviceException("Device update failed for Device Id: "+device.getId())); + } + + private void checkDevice(@NonNull Device device) { + List errors = new ArrayList<>(); + if (StringUtils.isBlank(device.getId())) errors.add("Null or blank Id"); + if (StringUtils.isBlank(device.getOwner())) errors.add("Null or blank Owner"); + if (device.getCreationDate()==null) errors.add("Null Creation date"); + if (device.getStatus()==null) errors.add("Null Status"); + if (!errors.isEmpty()) { + throw new DeviceException( + String.format("Device spec has errors: %s\n%s", + String.join(", ", errors), device)); + } + } + + public void deleteById(@NonNull String id) { + Optional result = getById(id); + if (result.isEmpty()) + throw new DeviceException( + "Device with the Id does not exists in repository: "+id); + deviceRepository.delete(result.get()); + result.get().setLastUpdateDate(Instant.now()); + } + + public void delete(@NonNull Device device) { + deviceRepository.deleteById(device.getId()); + device.setLastUpdateDate(Instant.now()); + } + + // ------------------------------------------------------------------------ + + public List getArchivedAll() { + return Collections.unmodifiableList(archivedDeviceRepository.findAll()); + } + + public List getArchivedByOwner(@NonNull String owner) { + return archivedDeviceRepository.findByOwner(owner); + } + + public Optional getArchivedById(@NonNull String id) { + return archivedDeviceRepository.findById(id); + } + + public List getArchivedByIpAddress(@NonNull String ipAddress) { + return archivedDeviceRepository.findByIpAddress(ipAddress); + } + + public void archiveDevice(String id) { + archiveRequestBySystem(id); + } + + public void archiveRequestBySystem(String id) { + Optional result = getById(id); + if (result.isEmpty()) + throw new DeviceException( + "Device with the Id does not exists in repository: " + id); + result.get().setArchiveDate(Instant.now()); + archivedDeviceRepository.save(deviceConversionService.toArchivedDevice(result.get())); + deviceRepository.delete(result.get()); + } + + public void unarchiveDevice(String id) { + Optional result = getArchivedById(id); + if (result.isEmpty()) + throw new DeviceException( + "Archived device with Id does not exists in repository: "+id); + + result.get().setArchiveDate(null); + deviceRepository.save(deviceConversionService.toDevice(result.get())); + archivedDeviceRepository.deleteById(result.get().getId()); + } +}