User Tools
Writing /app/www/public/data/meta/toolsandtechnologies/developing_in_new_rdf.meta failed
toolsandtechnologies:developing_in_new_rdf
Differences
This shows you the differences between two versions of the page.
| Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
| toolsandtechnologies:developing_in_new_rdf [2020/03/06 11:38] – akavanagh | toolsandtechnologies:developing_in_new_rdf [2021/06/25 10:09] (current) – external edit 127.0.0.1 | ||
|---|---|---|---|
| Line 1: | Line 1: | ||
| + | ====== Developing In The New RDF ====== | ||
| + | The RDF agent which discovers information on remote devices is centred around **Processors**. For the Agent to perform a discovery operation such as configuration discovery or topology discovery it uses processors defined in the RDF Agent for the given technology and version of the target device. Processors are developed per technology and per supported discovery operation. For example, see below. | ||
| + | **Example: ** Corning one supports Configuration, | ||
| + | |||
| + | The above example shows what is currently supported in the old RDF platform in the SNMP-Manager application. For this functionality to be re-written in the RDF Agent, a developer or operations engineer would need to define processors in the RDF Agent for each of these operations for this technology. Processors are defined as interfaces in the RDF agent for each of the given discovery operations - Configuration, | ||
| + | |||
| + | ==== RDF Agent Directory Structure ==== | ||
| + | In the RDF Agent project, processors are located in the **' | ||
| + | |||
| + | {{ : | ||
| + | |||
| + | |||
| + | ==== Processor Interfaces ==== | ||
| + | As processors are interfaces, all of these can be implemented within the RDF agent by a single Java class, across multiple Java classes. There is no right or wrong way to do this as it depends on the target device and technology. Please refer to the **Code and Design Best Practices & Principals** section of this wiki. All processors extend a common interface base known as **' | ||
| + | |||
| + | |||
| + | === MessageProcessorBase === | ||
| + | An example definition and explanation of each of the Message Processor Base Interface methods are outlined below. Every processor interface in the RDF agent implements this base interface so it is important to understand what each of these definitions are and what is expected in the implemented behaviour. The below image shows the interface method signatures. | ||
| + | |||
| + | {{ : | ||
| + | |||
| + | === Build Request Method === | ||
| + | * The 'Build Request' | ||
| + | * This argument contains all of the request information which was sent by the requesting application e.g target device IP address, technology, version etc | ||
| + | * The incoming message is used to form a set of tasks objects which are used to complete given discovery operations. | ||
| + | * This method returns a **Request** object which contains the list of defined tasks | ||
| + | * Your constructed task list would be a simple list such as List< | ||
| + | * Finally a call to **Request.builder().discoveryTasks(taskList).build()** | ||
| + | |||
| + | See below for an example of a definition of the ' | ||
| + | |||
| + | < | ||
| + | public Request buildRequest(IncomingMessage incomingMessage) { // | ||
| + | |||
| + | /*Incoming Message is populated by the Requesting Application i.e SNMP Manager*/ | ||
| + | |||
| + | List< | ||
| + | String ipAddress = incomingMessage.getHost(); | ||
| + | String snmpVersion = incomingMessage.getSnmpVersion(); | ||
| + | long timeout = incomingMessage.getTimeout(); | ||
| + | |||
| + | /* Paths in RDF agent To required MIBs */ | ||
| + | String mibPath = String.join(",", | ||
| + | COMBA_WIRELESS_REPEATER_MBDA_COMMINFO, | ||
| + | COMBA_WIRELESS_REPEATER_MBDA_RFINFO); | ||
| + | |||
| + | /* Query params have additional information about element | ||
| + | Map< | ||
| + | String discoveredName = ""; | ||
| + | |||
| + | if (queryParamsObject != null) { | ||
| + | discoveredName = incomingMessage.getQueryParams().get(" | ||
| + | } | ||
| + | |||
| + | /*Device Topology Is an object used to store discovery data */ | ||
| + | deviceTopology.setDiscoveredName(discoveredName); | ||
| + | deviceTopology.setIpAddresses(Arrays.asList(ipAddress)); | ||
| + | deviceTopology.setIsComponent(false); | ||
| + | deviceTopology.setHardwareType(" | ||
| + | |||
| + | /* Determine the type of discovery requested... Is it Config or Performance, | ||
| + | if (incomingMessage.getDiscoveryType() == IncomingMessage.DiscoveryType.CONFIGURATION) { | ||
| + | log.info(" | ||
| + | Map< | ||
| + | combaConfigParams.forEach((String oid, CombaConfigParameter value) -> taskList.add(createParamTask(oid, | ||
| + | } else if (incomingMessage.getDiscoveryType() == IncomingMessage.DiscoveryType.PERFORMANCE) { | ||
| + | log.info(" | ||
| + | Map< | ||
| + | combaPerformParams.forEach((String oid, CombaPerformParameter value) -> taskList.add(createParamTask(oid, | ||
| + | } | ||
| + | |||
| + | /*Create a Request Object to encapsulate your task(s) for the discovery operation | ||
| + | taskList.forEach( t-> log.info(" | ||
| + | return Request.builder().discoveryTasks(taskList).build(); | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | === Building Tasks === | ||
| + | Forming tasks to complete the definition of the Request Method can be done inline in the method itself or helper methods can be used. For example, if you want to perform a Topology discovery on a device, your formed tasks as HTTP could look like. | ||
| + | |||
| + | - Login. | ||
| + | - Retrieve Topology via HTTP. | ||
| + | - Logout. | ||
| + | |||
| + | If your RDF agent processor only does topology, it would be fine to create such tasks inline in the ' | ||
| + | |||
| + | < | ||
| + | private Task createLogoutTask(String url, HashMap< | ||
| + | MultiTechLoginRequestDTO dto = MultiTechLoginRequestDTO.builder().username(incomingMessage.getUsername()).password(incomingMessage.getPassword()).build(); | ||
| + | String logoutParams = gson.toJson(dto); | ||
| + | return Task.builder().name(" | ||
| + | .url(url + "/ | ||
| + | .headers(headers).httpRequestBody(logoutParams.getBytes()).build(); | ||
| + | } | ||
| + | |||
| + | private Task createLogoutOtherUserTask(String url, HashMap< | ||
| + | MultiTechLogoutRequestDTO dto = MultiTechLogoutRequestDTO.builder() | ||
| + | .logoutUser(incomingMessage.getUsername()) | ||
| + | .username(incomingMessage.getUsername()) | ||
| + | .password(incomingMessage.getPassword()).build(); | ||
| + | String logoutParams = gson.toJson(dto); | ||
| + | return Task.builder().name(" | ||
| + | .url(url + "/ | ||
| + | .headers(headers).httpRequestBody(logoutParams.getBytes()).build(); | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | == Process Response Method == | ||
| + | * This method definition is used to process and handle the data or results of your defined tasks. | ||
| + | * The return type of this method is used to capture any sub-tasks which may be needed as your tasks defined in ' | ||
| + | * Subtasks are not necessary so this can be null on method completion. | ||
| + | * Each of the tasks defined in ' | ||
| + | * The arguments for this method are the current task results and the next task results. In most instances, the next task reference is not needed or used in the current task execution. | ||
| + | * To store the results of these tasks(your discovery results) a class member object of type ' | ||
| + | * You don't know which task is currently being handled so it is typical to see logic is this method to check the tag of the current tag for its name, then it can be handled properly. | ||
| + | |||
| + | See below for an example of a definition of the ' | ||
| + | |||
| + | |||
| + | < | ||
| + | public Stack< | ||
| + | Stack< | ||
| + | if (task.hasErrors()) { /* If there are any errors | ||
| + | log.info(task.getName()); | ||
| + | log.error(task.getName()+" | ||
| + | errors.addErrors(task.getErrors()); | ||
| + | String errorsStr = Arrays.toString(task.getErrors().toArray()); | ||
| + | log.info(" | ||
| + | } else if (task.getName().contains(" | ||
| + | SNMPResponse configResponse = (SNMPResponse)task.getResponse(); | ||
| + | log.info(" | ||
| + | log.info(" | ||
| + | processConfigResponse(configResponse); | ||
| + | log.info(" | ||
| + | } else if (task.getName().contains(" | ||
| + | SNMPResponse performResponse = (SNMPResponse)task.getResponse(); | ||
| + | processPerformResponse(performResponse); | ||
| + | log.info(" | ||
| + | } | ||
| + | return subTasks; | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | === Get Errors Method === | ||
| + | * A simple getter definition for returning an Error object containing all encountered errors. | ||
| + | * Errors can occur in your defined tasks and when this occurs each error can be stored in a class member object called Errors. | ||
| + | * Errors are checked when handling Tasks in the ' | ||
| + | * Any recorded errors will be returned to the request application indicating there were some issues with the discovery process. | ||
| + | |||
| + | See below for an example of a definition of the ' | ||
| + | |||
| + | < | ||
| + | public Errors getErrors() { /* Interface Method */ | ||
| + | return errors; /* return the populated Errors object, this is populated in processResponse if there are errors */ | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | === Is Compatible === | ||
| + | * Is Compatible is used so the RDF Agent knows which processor it can use to complete the discovery request. | ||
| + | * The requesting application can ask for discovery on a device in which the technology or version is not currently supported, this method allows processors to define if they support a particular technology and version. | ||
| + | * The arguments to the ' | ||
| + | * The incoming message passed from the requesting application contains information on the target device such as technology and version, this information is used to return a true or false determination if a processor supports this device or not. | ||
| + | |||
| + | < | ||
| + | public boolean isCompatible(String technologyName, | ||
| + | return name.equals(technologyName) && supportedVersion.isCompatible(version); | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | ==== Processor Specific Interfaces ==== | ||
| + | As mentioned in the above sections, all of these methods are extended by specific processor interfaces for each of the discovery operations such as topology, alarm sync, config or performance. This section will go over the additional methods which need to be overridden when extending such interfaces. | ||
| + | |||
| + | == Config Processor == | ||
| + | |||
| + | * Getter which returns a CommonDeviceTopology object which can contain the discovered configuration parameters. | ||
| + | |||
| + | < | ||
| + | private CommonDeviceTopology response = new CommonDeviceTopology(); | ||
| + | | ||
| + | /*REST OF PROCESSOR - CommonDeviceTopology gets populated by Tasks */ | ||
| + | |||
| + | /* Interface method for ConfigProcessor, | ||
| + | public CommonDeviceTopology getConfigs() { | ||
| + | | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | == Performance Processor == | ||
| + | * Getter which returns a CommonDeviceTopology object which can contain the discovered configuration parameters. | ||
| + | * Return type is object, but return a CommonDevice Topology containing performance parameters. | ||
| + | |||
| + | < | ||
| + | private CommonDeviceTopology response = new CommonDeviceTopology(); | ||
| + | | ||
| + | /*REST OF PROCESSOR - CommonDeviceTopology gets populated by Tasks */ | ||
| + | |||
| + | public interface PerformanceProcessor extends MessageProcessorBase { | ||
| + | public Object getPerformance(); | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | == Topology Processor == | ||
| + | * Again, the same interface as Config and Performance, | ||
| + | |||
| + | < | ||
| + | private CommonDeviceTopology response = new CommonDeviceTopology(); | ||
| + | | ||
| + | /*REST OF PROCESSOR - CommonDeviceTopology gets populated by Tasks */ | ||
| + | |||
| + | public CommonDeviceTopology getTopology() { | ||
| + | return deviceTopology; | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | == Alarm Processor == | ||
| + | * A recurring theme, the same interface as Config, Performance and Topology but different method name. | ||
| + | * Returns a CommonDeviceTopology object containing alarm data. | ||
| + | |||
| + | < | ||
| + | private CommonDeviceTopology response = new CommonDeviceTopology(); | ||
| + | | ||
| + | /*REST OF PROCESSOR - CommonDeviceTopology gets populated by Tasks */ | ||
| + | public CommonDeviceTopology getAlarms() { | ||
| + | return response; | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | ==== Common Device Topology Object & Properties ==== | ||
| + | Don't get confused with the word topology in the name, this object is for storing the results of **ANY** discovery operation e.g Config, Performance, | ||
| + | |||
| + | < | ||
| + | private String name; /* Device Name on Parent Device e.g DAS Controller */ | ||
| + | private String discoveredName; | ||
| + | private List< | ||
| + | private List< | ||
| + | private List< | ||
| + | private String hardwareType; | ||
| + | private String elementType; | ||
| + | private List< | ||
| + | private Boolean isComponent; | ||
| + | </ | ||