blob: 9f2254cbbb1483f8c0aff9cea6b4ccf60f83e2cd [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
MaoLu3308ca42017-05-10 20:18:54 -0700144 public CompletableFuture<Void> switchToForce(ConnectPoint identifier, int index) {
145 return getProtectionEndpointConfig(identifier)
146 .thenApply(m -> m.paths().get(index))
147 .thenApply(m -> switchDevice(formatOperation(m.output().connectPoint().port(), KEY_OPT_FORCE)))
148 .thenApply(m -> null);
149 }
150
151 @Override
152 public CompletableFuture<Void> switchToManual(ConnectPoint identifier, int index) {
153 return getProtectionEndpointConfig(identifier)
154 .thenApply(m -> m.paths().get(index))
155 .thenApply(m -> switchDevice(formatOperation(m.output().connectPoint().port(), KEY_OPT_MANUAL)))
156 .thenApply(m -> null);
157 }
158
159 @Override
160 public CompletableFuture<Void> switchToAutomatic(ConnectPoint identifier) {
161 switchDevice(KEY_OPT_AUTO);
162 return CompletableFuture.completedFuture(null);
163 }
164
165 private ProtectedTransportEndpointState buildProtectedTransportEndpointState() {
166 // First, get active port from device.
167 PortNumber activePort = acquireActivePort();
168 // Build all endpoint state with port working attribute.
169 List<TransportEndpointState> states = new ArrayList<>();
170 states.add(buildTransportEndpointState(data().deviceId(), PORT_PRIMARY, activePort));
171 states.add(buildTransportEndpointState(data().deviceId(), PORT_SECONDARY, activePort));
172 return ProtectedTransportEndpointState.builder()
173 .withPathStates(states)
174 .withDescription(buildProtectedTransportEndpointDescription())
175 .withActivePathIndex(getActiveIndex(states, activePort))
176 .build();
177 }
178
179 private ProtectedTransportEndpointDescription buildProtectedTransportEndpointDescription() {
180 List<TransportEndpointDescription> descs = new ArrayList<>();
181 descs.add(buildTransportEndpointDescription(data().deviceId(), PORT_PRIMARY));
182 descs.add(buildTransportEndpointDescription(data().deviceId(), PORT_SECONDARY));
183 return ProtectedTransportEndpointDescription.of(descs, getPeerId(), FINGERPRINT);
184 }
185
186 private TransportEndpointDescription buildTransportEndpointDescription(DeviceId id, PortNumber port) {
187 return TransportEndpointDescription.builder()
188 .withOutput(new FilteredConnectPoint(new ConnectPoint(id, port)))
189 .build();
190 }
191
192 private TransportEndpointState buildTransportEndpointState(
193 DeviceId id, PortNumber port, PortNumber activePort) {
194 String status = port.equals(activePort) ? STATUS_IN_SERVICE : STATUS_OUT_SERVICE;
195 Map<String, String> attributes = new HashMap<>();
196 attributes.put(INPUT_PORT_STATUS, status);
197 return TransportEndpointState.builder()
198 .withId(TransportEndpointId.of(port.name()))
199 .withDescription(buildTransportEndpointDescription(id, port))
200 .addAttributes(attributes)
201 .build();
202 }
203
204 private int getActiveIndex(List<TransportEndpointState> pathStates, PortNumber activePort) {
205 int activeIndex = 0;
206 for (TransportEndpointState state : pathStates) {
207 if (activePort.equals(state.description().output().connectPoint().port())) {
208 return activeIndex;
209 }
210 ++activeIndex;
211 }
212 return ProtectedTransportEndpointState.ACTIVE_UNKNOWN;
213 }
214
215 private PortNumber acquireActivePort() {
216 String filter = new StringBuilder(xmlOpen(KEY_OPENOPTICALDEV_XMLNS))
217 .append(xmlOpen(KEY_STATE))
218 .append(xmlEmpty(KEY_OPSSTATE))
219 .append(xmlClose(KEY_STATE))
220 .append(xmlClose(KEY_OPENOPTICALDEV))
221 .toString();
222 String reply = netconfGet(handler(), filter);
223 log.debug("Service state replying, {}", reply);
224 return reply.contains(KEY_NAME_PRIMARY) ? PORT_PRIMARY : PORT_SECONDARY;
225 }
226
227 private String formatOperation(PortNumber port, String operation) {
228 String key = port.name().contains(KEY_NAME_PRIMARY) ? KEY_NAME_PRIMARY : KEY_NAME_SECONDARY;
229 return String.format(FMT_OPT, operation, key);
230 }
231
232 private boolean switchDevice(String operation) {
233 log.debug("Switch to {} for Device {}", operation, data().deviceId());
234 String cfg = new StringBuilder(xmlOpen(KEY_OPENOPTICALDEV_XMLNS))
235 .append(xmlOpen(KEY_CONFIG))
236 .append(xml(KEY_OPSCONFIG, operation))
237 .append(xmlClose(KEY_CONFIG))
238 .append(xmlClose(KEY_OPENOPTICALDEV))
239 .toString();
240 return netconfEditConfig(handler(), CFG_MODE_MERGE, cfg);
241 }
242
243 private void addLink(DeviceId peerId) {
244 if (peerId == null) {
245 log.warn("PeerID is null for device {}", data().deviceId());
246 return;
247 }
248 LinkKey link = linkKey(new ConnectPoint(peerId, PORT_VIRTUAL),
249 new ConnectPoint(data().deviceId(), PORT_VIRTUAL));
250 handler().get(NetworkConfigService.class).addConfig(link, BasicLinkConfig.class)
251 .type(Link.Type.VIRTUAL)
252 .apply();
253 }
254
255 private void removeLink(DeviceId peerId) {
256 if (peerId == null) {
257 log.warn("PeerID is null for device {}", data().deviceId());
258 return;
259 }
260 LinkKey link = linkKey(new ConnectPoint(peerId, PORT_VIRTUAL),
261 new ConnectPoint(data().deviceId(), PORT_VIRTUAL));
262 handler().get(NetworkConfigService.class).removeConfig(link, BasicLinkConfig.class);
263 }
264
265 private DeviceId getPeerId() {
266 ConnectPoint dstCp = new ConnectPoint(data().deviceId(), PORT_VIRTUAL);
267 Set<Link> links = handler().get(LinkService.class).getIngressLinks(dstCp);
268 for (Link l : links) {
269 if (l.type() == Link.Type.VIRTUAL) {
Frank Wangd8ab0962017-08-11 11:09:30 +0800270 // This device is the destination and peer is the source.
MaoLu3308ca42017-05-10 20:18:54 -0700271 return l.src().deviceId();
272 }
273 }
274 // None of link found, return itself.
275 return data().deviceId();
276 }
277}