blob: 792b46c18b13a308db32490c9112c5ccb5f791af [file] [log] [blame]
Yi Tseng9619d802020-03-19 23:28:31 +08001/*
2 * Copyright 2020-present Open Networking Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 * This work was partially supported by EC H2020 project METRO-HAUL (761727).
17 */
18
19package org.onosproject.drivers.odtn.openconfig;
20
21import com.google.common.collect.ImmutableSet;
22import com.google.common.collect.Range;
23import gnmi.Gnmi;
24import org.onosproject.gnmi.api.GnmiClient;
25import org.onosproject.gnmi.api.GnmiController;
26import org.onosproject.gnmi.api.GnmiUtils.GnmiPathBuilder;
27import org.onosproject.grpc.utils.AbstractGrpcHandlerBehaviour;
28import org.onosproject.net.Port;
29import org.onosproject.net.PortNumber;
30import org.onosproject.net.behaviour.PowerConfig;
31import org.slf4j.Logger;
32import org.slf4j.LoggerFactory;
33
34import java.util.Collection;
35import java.util.Optional;
36import java.util.concurrent.ExecutionException;
37
38/**
39 * PowerConfig behaviour for gNMI and OpenConfig model based device.
40 */
41public class GnmiTerminalDevicePowerConfig<T>
42 extends AbstractGrpcHandlerBehaviour<GnmiClient, GnmiController>
43 implements PowerConfig<T> {
44
45 private static final Logger log = LoggerFactory.getLogger(GnmiTerminalDevicePowerConfig.class);
46 private static final int DEFAULT_OC_POWER_PRECISION = 2;
47 private static final Collection<Port.Type> OPTICAL_TYPES = ImmutableSet.of(Port.Type.FIBER,
48 Port.Type.PACKET,
49 Port.Type.ODUCLT,
50 Port.Type.OCH,
51 Port.Type.OMS,
52 Port.Type.OTU);
53
54 public GnmiTerminalDevicePowerConfig() {
55 super(GnmiController.class);
56 }
57
58 @Override
59 public Optional<Double> getTargetPower(PortNumber port, T component) {
60 if (!setupBehaviour("getTargetPower")) {
61 return Optional.empty();
62 }
63 if (!isOpticalPort(port)) {
64 return Optional.empty();
65 }
66 // path: /components/component[name=<name>]/optical-channel/config/target-output-power
67 return getValueFromPath(getOcName(port), "config/target-output-power");
68 }
69
70 @Override
71 public void setTargetPower(PortNumber port, T component, double power) {
72 if (!setupBehaviour("setTargetPower")) {
73 return;
74 }
75 if (!isOpticalPort(port)) {
76 return;
77 }
78 setValueToPath(getOcName(port), "config/target-output-power", power);
79 }
80
81 @Override
82 public Optional<Double> currentPower(PortNumber port, T component) {
83 if (!setupBehaviour("currentPower")) {
84 return Optional.empty();
85 }
86 if (!isOpticalPort(port)) {
87 return Optional.empty();
88 }
89 // path: /components/component[name=<name>]/optical-channel/state/output-power/instant
90 return getValueFromPath(getOcName(port), "state/output-power/instant");
91 }
92
93 @Override
94 public Optional<Double> currentInputPower(PortNumber port, T component) {
95 if (!setupBehaviour("currentInputPower")) {
96 return Optional.empty();
97 }
98 if (!isOpticalPort(port)) {
99 return Optional.empty();
100 }
101 // path: /components/component[name=<name>]/optical-channel/state/input-power/instant
102 return getValueFromPath(getOcName(port), "state/input-power/instant");
103 }
104
105 @Override
106 public Optional<Range<Double>> getTargetPowerRange(PortNumber port, Object component) {
107 if (!isOpticalPort(port)) {
108 return Optional.empty();
109 }
110
111 // From CassiniTerminalDevicePowerConfig
112 double targetMin = -30;
113 double targetMax = 1;
114 return Optional.of(Range.open(targetMin, targetMax));
115 }
116
117 @Override
118 public Optional<Range<Double>> getInputPowerRange(PortNumber port, Object component) {
119 if (!isOpticalPort(port)) {
120 return Optional.empty();
121 }
122
123 // From CassiniTerminalDevicePowerConfig
124 double targetMin = -30;
125 double targetMax = 1;
126 return Optional.of(Range.open(targetMin, targetMax));
127 }
128
129 private String getOcName(PortNumber portNumber) {
130 if (!setupBehaviour("getOcName")) {
131 return null;
132 }
133 return deviceService.getPort(deviceId, portNumber).annotations().value("oc-name");
134 }
135
136 private boolean isOpticalPort(PortNumber portNumber) {
137 if (!setupBehaviour("isOpticalPort")) {
138 return false;
139 }
140 return OPTICAL_TYPES.contains(deviceService.getPort(deviceId, portNumber).type());
141 }
142
143 private Optional<Double> getValueFromPath(String ocName, String subPath) {
144 Gnmi.GetRequest req = Gnmi.GetRequest.newBuilder()
145 .addPath(buildPathWithSubPath(ocName, subPath))
146 .setEncoding(Gnmi.Encoding.PROTO)
147 .build();
148 try {
149 Gnmi.GetResponse resp = client.get(req).get();
150 // Here we assume we have only one response
151 if (resp.getNotificationCount() == 0 || resp.getNotification(0).getUpdateCount() == 0) {
152 log.warn("Empty response for sub-path {}, component {}", subPath, ocName);
153 return Optional.empty();
154 }
155 Gnmi.Update update = resp.getNotification(0).getUpdate(0);
156 Gnmi.Decimal64 value = update.getVal().getDecimalVal();
157 return Optional.of(decimal64ToDouble(value));
158 } catch (ExecutionException | InterruptedException e) {
159 log.warn("Unable to get value from optical sub-path {} for component {}: {}",
160 subPath, ocName, e.getMessage());
161 return Optional.empty();
162 }
163 }
164
165 private void setValueToPath(String ocName, String subPath, Double value) {
166 Gnmi.TypedValue val = Gnmi.TypedValue.newBuilder()
167 .setDecimalVal(doubleToDecimal64(value, DEFAULT_OC_POWER_PRECISION))
168 .build();
169 Gnmi.Update update = Gnmi.Update.newBuilder()
170 .setPath(buildPathWithSubPath(ocName, subPath))
171 .setVal(val)
172 .build();
173 Gnmi.SetRequest req = Gnmi.SetRequest.newBuilder()
174 .addUpdate(update)
175 .build();
176 try {
177 client.set(req).get();
178 } catch (ExecutionException | InterruptedException e) {
179 log.warn("Unable to set optical sub-path {}, component {}, value {}: {}",
180 subPath, ocName, value, e.getMessage());
181 }
182 }
183
184 private Gnmi.Path buildPathWithSubPath(String ocName, String subPath) {
185 String[] elems = subPath.split("/");
186 GnmiPathBuilder pathBuilder = GnmiPathBuilder.newBuilder()
187 .addElem("components")
188 .addElem("component").withKeyValue("name", ocName)
189 .addElem("optical-channel");
190 for (String elem : elems) {
191 pathBuilder.addElem(elem);
192 }
193 return pathBuilder.build();
194 }
195
196 private Double decimal64ToDouble(Gnmi.Decimal64 value) {
197 double result = value.getDigits();
198 if (value.getPrecision() != 0) {
199 result = result / Math.pow(10, value.getPrecision());
200 }
201 return result;
202 }
203
204 private Gnmi.Decimal64 doubleToDecimal64(Double value, int precision) {
205 return Gnmi.Decimal64.newBuilder()
206 .setDigits((long) (value * Math.pow(10, precision)))
207 .setPrecision(precision)
208 .build();
209 }
210}