blob: 677d69457fcf307818f64e808f0d8d7564182a8d [file] [log] [blame]
MaoLu3308ca42017-05-10 20:18:54 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2016 Open Networking Foundation
MaoLu3308ca42017-05-10 20:18:54 -07003 *
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 */
16package org.onosproject.drivers.oplink;
17
18import org.onosproject.net.ConnectPoint;
19import org.onosproject.net.DeviceId;
20import org.onosproject.net.FilteredConnectPoint;
21import org.onosproject.net.Link;
22import org.onosproject.net.LinkKey;
23import org.onosproject.net.PortNumber;
24import org.onosproject.net.behaviour.protection.ProtectedTransportEndpointDescription;
25import org.onosproject.net.behaviour.protection.ProtectedTransportEndpointState;
26import org.onosproject.net.behaviour.protection.ProtectionConfigBehaviour;
27import org.onosproject.net.behaviour.protection.TransportEndpointDescription;
28import org.onosproject.net.behaviour.protection.TransportEndpointId;
29import org.onosproject.net.behaviour.protection.TransportEndpointState;
30import org.onosproject.net.config.NetworkConfigService;
31import org.onosproject.net.config.basics.BasicLinkConfig;
32import org.onosproject.net.driver.AbstractHandlerBehaviour;
33import org.onosproject.net.link.LinkService;
34import org.slf4j.Logger;
35
36import java.util.ArrayList;
37import java.util.HashMap;
38import java.util.List;
39import java.util.Map;
40import java.util.Set;
41import java.util.concurrent.CompletableFuture;
42
43import static org.onosproject.drivers.oplink.OplinkNetconfUtility.*;
44import static org.onosproject.net.LinkKey.linkKey;
45import static org.onosproject.net.optical.OpticalAnnotations.INPUT_PORT_STATUS;
46import static org.onosproject.net.optical.OpticalAnnotations.STATUS_IN_SERVICE;
47import static org.onosproject.net.optical.OpticalAnnotations.STATUS_OUT_SERVICE;
48import static org.slf4j.LoggerFactory.getLogger;
49
50/**
51 * Implementations of the protection behaviours for Oplink Optical Protection Switch (OPS).
52 * _____________________
53 * | | 4
54 * CLIENT RX| ---50%-----|----------- PRIMARY TX
55 * -----------|---------- |
56 * 1 | ---50%-----|----------- SECONDARY TX
57 * | | 6
58 * | OPLINK OPS BOX |
59 * | ___________ | 3
60 * CLIENT TX| | |----|----------- PRIMARY RX
61 * -----------|----| SWITCH | |
62 * 2 | |___________|----|----------- SECONDARY RX
63 * |_____________________| 5
64 *
65 * - Oplink OPS has 6 uni-directional physical ports:
66 * - port 2 is connected to the client side port.
67 * - port 3 is primary port for network side.
68 * - port 5 is secondary port for network side.
69 * - port 1 directly connects to port 4 and 5 with 50/50 split light.
70 * - Traffic protection
71 * - Traffic(Optical light) from client port is broadcasted (50/50 split) to
72 * both primary and secondary ports all the time.
73 * - In fault free condition, traffic from primary port is bridged to client port and
74 * in the case of primary port fails (LOS), traffic is bridged from secondary port to client port.
75 * - User initiated switch (to primary or secondary) is also supported.
76 */
77public class OplinkOpticalProtectionSwitchConfig extends AbstractHandlerBehaviour
78 implements ProtectionConfigBehaviour {
79
80 // key nodes
81 private static final String KEY_CONFIG = "config";
82 private static final String KEY_OPSCONFIG = "ops-config";
83 private static final String KEY_STATE = "state";
84 private static final String KEY_OPSSTATE = "ops-state";
85 private static final String KEY_NAME_PRIMARY = "primary";
86 private static final String KEY_NAME_SECONDARY = "secondary";
87 private static final String KEY_OPT_FORCE = "force";
88 private static final String KEY_OPT_MANUAL = "manual";
89 private static final String KEY_OPT_AUTO = "auto-revertive";
90
91 // operation format: [OPT]-[NAME], eg. force-primary
92 private static final String FMT_OPT = "%s-%s";
93
94 // define virtual port number
95 private static final PortNumber PORT_VIRTUAL = PortNumber.portNumber(0);
96 private static final PortNumber PORT_PRIMARY = PortNumber.portNumber(3, "primary_port");
97 private static final PortNumber PORT_SECONDARY = PortNumber.portNumber(5, "secondary_port");
98 // log
99 private static final Logger log = getLogger(OplinkOpticalProtectionSwitchConfig.class);
100
101 @Override
102 public CompletableFuture<ConnectPoint> createProtectionEndpoint(
103 ProtectedTransportEndpointDescription configuration) {
104 // Add a virtual link between two virtual ports of the device and peer.
105 addLink(getPeerId());
106 // Only support one group in the device.
107 return CompletableFuture.completedFuture(new ConnectPoint(data().deviceId(), PORT_VIRTUAL));
108 }
109
110 @Override
111 public CompletableFuture<ConnectPoint> updateProtectionEndpoint(ConnectPoint identifier,
112 ProtectedTransportEndpointDescription configuration) {
113 // The function of updating protection virtual Port is not supported by the device.
114 return CompletableFuture.completedFuture(identifier);
115 }
116
117 @Override
118 public CompletableFuture<Boolean> deleteProtectionEndpoint(ConnectPoint identifier) {
119 if (identifier.port().equals(PORT_VIRTUAL)) {
120 // Remove the virtual link from peer to the virtual port.
121 removeLink(getPeerId());
122 return CompletableFuture.completedFuture(true);
123 }
124 return CompletableFuture.completedFuture(false);
125 }
126
127 @Override
128 public CompletableFuture<Map<ConnectPoint, ProtectedTransportEndpointDescription>>
129 getProtectionEndpointConfigs() {
130 // There are two underlying transport entity endpoints in the device.
131 Map<ConnectPoint, ProtectedTransportEndpointDescription> map = new HashMap<>();
132 map.put(new ConnectPoint(data().deviceId(), PORT_VIRTUAL), buildProtectedTransportEndpointDescription());
133 return CompletableFuture.completedFuture(map);
134 }
135
136 @Override
137 public CompletableFuture<Map<ConnectPoint, ProtectedTransportEndpointState>> getProtectionEndpointStates() {
138 Map<ConnectPoint, ProtectedTransportEndpointState> map = new HashMap<>();
139 map.put(new ConnectPoint(data().deviceId(), PORT_VIRTUAL), buildProtectedTransportEndpointState());
140 return CompletableFuture.completedFuture(map);
141 }
142
143 @Override
144 public CompletableFuture<Void> switchWorkingPath(ConnectPoint identifier, int index) {
145 return switchToManual(identifier, index);
146 }
147
148 @Override
149 public CompletableFuture<Void> switchToForce(ConnectPoint identifier, int index) {
150 return getProtectionEndpointConfig(identifier)
151 .thenApply(m -> m.paths().get(index))
152 .thenApply(m -> switchDevice(formatOperation(m.output().connectPoint().port(), KEY_OPT_FORCE)))
153 .thenApply(m -> null);
154 }
155
156 @Override
157 public CompletableFuture<Void> switchToManual(ConnectPoint identifier, int index) {
158 return getProtectionEndpointConfig(identifier)
159 .thenApply(m -> m.paths().get(index))
160 .thenApply(m -> switchDevice(formatOperation(m.output().connectPoint().port(), KEY_OPT_MANUAL)))
161 .thenApply(m -> null);
162 }
163
164 @Override
165 public CompletableFuture<Void> switchToAutomatic(ConnectPoint identifier) {
166 switchDevice(KEY_OPT_AUTO);
167 return CompletableFuture.completedFuture(null);
168 }
169
170 private ProtectedTransportEndpointState buildProtectedTransportEndpointState() {
171 // First, get active port from device.
172 PortNumber activePort = acquireActivePort();
173 // Build all endpoint state with port working attribute.
174 List<TransportEndpointState> states = new ArrayList<>();
175 states.add(buildTransportEndpointState(data().deviceId(), PORT_PRIMARY, activePort));
176 states.add(buildTransportEndpointState(data().deviceId(), PORT_SECONDARY, activePort));
177 return ProtectedTransportEndpointState.builder()
178 .withPathStates(states)
179 .withDescription(buildProtectedTransportEndpointDescription())
180 .withActivePathIndex(getActiveIndex(states, activePort))
181 .build();
182 }
183
184 private ProtectedTransportEndpointDescription buildProtectedTransportEndpointDescription() {
185 List<TransportEndpointDescription> descs = new ArrayList<>();
186 descs.add(buildTransportEndpointDescription(data().deviceId(), PORT_PRIMARY));
187 descs.add(buildTransportEndpointDescription(data().deviceId(), PORT_SECONDARY));
188 return ProtectedTransportEndpointDescription.of(descs, getPeerId(), FINGERPRINT);
189 }
190
191 private TransportEndpointDescription buildTransportEndpointDescription(DeviceId id, PortNumber port) {
192 return TransportEndpointDescription.builder()
193 .withOutput(new FilteredConnectPoint(new ConnectPoint(id, port)))
194 .build();
195 }
196
197 private TransportEndpointState buildTransportEndpointState(
198 DeviceId id, PortNumber port, PortNumber activePort) {
199 String status = port.equals(activePort) ? STATUS_IN_SERVICE : STATUS_OUT_SERVICE;
200 Map<String, String> attributes = new HashMap<>();
201 attributes.put(INPUT_PORT_STATUS, status);
202 return TransportEndpointState.builder()
203 .withId(TransportEndpointId.of(port.name()))
204 .withDescription(buildTransportEndpointDescription(id, port))
205 .addAttributes(attributes)
206 .build();
207 }
208
209 private int getActiveIndex(List<TransportEndpointState> pathStates, PortNumber activePort) {
210 int activeIndex = 0;
211 for (TransportEndpointState state : pathStates) {
212 if (activePort.equals(state.description().output().connectPoint().port())) {
213 return activeIndex;
214 }
215 ++activeIndex;
216 }
217 return ProtectedTransportEndpointState.ACTIVE_UNKNOWN;
218 }
219
220 private PortNumber acquireActivePort() {
221 String filter = new StringBuilder(xmlOpen(KEY_OPENOPTICALDEV_XMLNS))
222 .append(xmlOpen(KEY_STATE))
223 .append(xmlEmpty(KEY_OPSSTATE))
224 .append(xmlClose(KEY_STATE))
225 .append(xmlClose(KEY_OPENOPTICALDEV))
226 .toString();
227 String reply = netconfGet(handler(), filter);
228 log.debug("Service state replying, {}", reply);
229 return reply.contains(KEY_NAME_PRIMARY) ? PORT_PRIMARY : PORT_SECONDARY;
230 }
231
232 private String formatOperation(PortNumber port, String operation) {
233 String key = port.name().contains(KEY_NAME_PRIMARY) ? KEY_NAME_PRIMARY : KEY_NAME_SECONDARY;
234 return String.format(FMT_OPT, operation, key);
235 }
236
237 private boolean switchDevice(String operation) {
238 log.debug("Switch to {} for Device {}", operation, data().deviceId());
239 String cfg = new StringBuilder(xmlOpen(KEY_OPENOPTICALDEV_XMLNS))
240 .append(xmlOpen(KEY_CONFIG))
241 .append(xml(KEY_OPSCONFIG, operation))
242 .append(xmlClose(KEY_CONFIG))
243 .append(xmlClose(KEY_OPENOPTICALDEV))
244 .toString();
245 return netconfEditConfig(handler(), CFG_MODE_MERGE, cfg);
246 }
247
248 private void addLink(DeviceId peerId) {
249 if (peerId == null) {
250 log.warn("PeerID is null for device {}", data().deviceId());
251 return;
252 }
253 LinkKey link = linkKey(new ConnectPoint(peerId, PORT_VIRTUAL),
254 new ConnectPoint(data().deviceId(), PORT_VIRTUAL));
255 handler().get(NetworkConfigService.class).addConfig(link, BasicLinkConfig.class)
256 .type(Link.Type.VIRTUAL)
257 .apply();
258 }
259
260 private void removeLink(DeviceId peerId) {
261 if (peerId == null) {
262 log.warn("PeerID is null for device {}", data().deviceId());
263 return;
264 }
265 LinkKey link = linkKey(new ConnectPoint(peerId, PORT_VIRTUAL),
266 new ConnectPoint(data().deviceId(), PORT_VIRTUAL));
267 handler().get(NetworkConfigService.class).removeConfig(link, BasicLinkConfig.class);
268 }
269
270 private DeviceId getPeerId() {
271 ConnectPoint dstCp = new ConnectPoint(data().deviceId(), PORT_VIRTUAL);
272 Set<Link> links = handler().get(LinkService.class).getIngressLinks(dstCp);
273 for (Link l : links) {
274 if (l.type() == Link.Type.VIRTUAL) {
Frank Wangd8ab0962017-08-11 11:09:30 +0800275 // This device is the destination and peer is the source.
MaoLu3308ca42017-05-10 20:18:54 -0700276 return l.src().deviceId();
277 }
278 }
279 // None of link found, return itself.
280 return data().deviceId();
281 }
282}