blob: e17ddf645f1dc88238c43367697fe81063a80462 [file] [log] [blame]
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -07001/*
2 * Copyright 2018-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 */
16package org.onosproject.odtn.cli.impl;
17
18import static org.onosproject.odtn.utils.YangToolUtil.toCharSequence;
19import static org.onosproject.odtn.utils.YangToolUtil.toCompositeData;
20import static org.onosproject.odtn.utils.YangToolUtil.toDocument;
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -070021import static org.onosproject.odtn.utils.YangToolUtil.toResourceData;
22import static org.onosproject.odtn.utils.YangToolUtil.toXmlCompositeStream;
23import static org.slf4j.LoggerFactory.getLogger;
24
25import java.util.ArrayList;
26import java.util.Arrays;
27import java.util.List;
28import java.util.Optional;
29import java.util.regex.Pattern;
30
31import javax.xml.parsers.DocumentBuilderFactory;
32import javax.xml.parsers.ParserConfigurationException;
33
Ray Milkey86ad7bb2018-09-27 12:32:28 -070034import org.apache.karaf.shell.api.action.Argument;
35import org.apache.karaf.shell.api.action.Command;
Ray Milkey64a05892018-10-09 15:23:52 -070036import org.apache.karaf.shell.api.action.Completion;
Ray Milkey86ad7bb2018-09-27 12:32:28 -070037import org.apache.karaf.shell.api.action.Option;
Ray Milkey7a2dee52018-09-28 10:58:28 -070038import org.apache.karaf.shell.api.action.lifecycle.Service;
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -070039import org.onlab.util.XmlString;
40import org.onosproject.cli.AbstractShellCommand;
41import org.onosproject.cli.net.DeviceIdCompleter;
Yuta HIGUCHI9ee86642018-05-15 15:25:00 -070042import org.onosproject.cli.net.PortNumberCompleter;
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -070043import org.onosproject.config.DynamicConfigService;
44import org.onosproject.net.DeviceId;
Yuta HIGUCHI9ee86642018-05-15 15:25:00 -070045import org.onosproject.net.PortNumber;
Yuta HIGUCHId0f8d892018-04-09 12:14:00 -070046import org.onosproject.net.device.DeviceService;
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -070047import org.onosproject.netconf.NetconfController;
48import org.onosproject.netconf.NetconfDevice;
49import org.onosproject.netconf.NetconfException;
Yuta HIGUCHId0f8d892018-04-09 12:14:00 -070050import org.onosproject.odtn.behaviour.ConfigurableTransceiver;
51import org.onosproject.odtn.behaviour.PlainTransceiver;
52import org.onosproject.odtn.utils.openconfig.OpticalChannel;
53import org.onosproject.odtn.utils.openconfig.Transceiver;
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -070054import org.onosproject.yang.model.DataNode;
55import org.onosproject.yang.model.ResourceId;
56import org.slf4j.Logger;
57import org.w3c.dom.Document;
58import org.w3c.dom.Element;
59
Yuta HIGUCHId0f8d892018-04-09 12:14:00 -070060import com.google.common.collect.Lists;
61import com.google.common.io.CharSource;
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -070062
Ray Milkey7a2dee52018-09-28 10:58:28 -070063@Service
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -070064@Command(scope = "onos", name = "odtn-manual-test",
65 description = "ODTN manual test command")
66public class OdtnManualTestCommand extends AbstractShellCommand {
67
68 private static final Logger log = getLogger(OdtnManualTestCommand.class);
69
70 public static enum Mode {
71 ENABLE_TRANSCEIVER,
72 DISABLE_TRANSCEIVER,
73 PRECONF_TRANSCEIVER,
74 PRECONF_OPTICAL_CHANNEL,
75 }
76
77 ModeCompleter modeCompleter;
78 @Argument(index = 0, name = "mode", description = "one of Mode see source",
79 required = true)
Ray Milkey64a05892018-10-09 15:23:52 -070080 @Completion(ModeCompleter.class)
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -070081 String modeStr = Mode.ENABLE_TRANSCEIVER.name();
82 Mode mode;
83
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -070084 @Option(name = "--deviceId", description = "Device ID URI to send configuration to",
85 required = false)
Ray Milkey64a05892018-10-09 15:23:52 -070086 @Completion(DeviceIdCompleter.class)
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -070087 String uri = null;
88
Yuta HIGUCHI9ee86642018-05-15 15:25:00 -070089 // Note: this will required Port information in device subystem
Yuta HIGUCHI01c66eb2018-05-22 12:12:35 -070090 @Option(name = "--cltPortNo", description = "Client-side PortNumber to send configuration to",
Yuta HIGUCHI9ee86642018-05-15 15:25:00 -070091 required = false)
Ray Milkey64a05892018-10-09 15:23:52 -070092 @Completion(PortNumberCompleter.class)
Yuta HIGUCHI01c66eb2018-05-22 12:12:35 -070093 String cltPortNo = null;
94
95 @Option(name = "--linePortNo", description = "Line-side PortNumber to send configuration to",
96 required = false)
Ray Milkey64a05892018-10-09 15:23:52 -070097 @Completion(PortNumberCompleter.class)
Yuta HIGUCHI01c66eb2018-05-22 12:12:35 -070098 String linePortNo = null;
Yuta HIGUCHI9ee86642018-05-15 15:25:00 -070099
100
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700101 // TODO add completer for this?
102 @Option(name = "--component",
103 description = "Component name",
104 required = false, multiValued = false)
105 private String componentName = "TRANSCEIVER_1_1_4_1";
106
107
108 // OSGi Services to be filled in at the beginning.
109 private DynamicConfigService dcs;
Yuta HIGUCHId0f8d892018-04-09 12:14:00 -0700110 private DeviceService deviceService;
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700111
112
113 void printlog(String format, Object... objs) {
114 print(format.replaceAll(Pattern.quote("{}"), "%s"), objs);
115 log.info(format, objs);
116 }
117
Yuta HIGUCHId0f8d892018-04-09 12:14:00 -0700118 List<CharSequence> transform(List<DataNode> input) {
119 ResourceId empty = ResourceId.builder().build();
120 return Lists.transform(input,
121 node -> toCharSequence(toXmlCompositeStream(toCompositeData(toResourceData(empty, node)))));
122 }
123
124
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700125 @Override
Ray Milkey86ad7bb2018-09-27 12:32:28 -0700126 protected void doExecute() {
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700127 dcs = get(DynamicConfigService.class);
Yuta HIGUCHId0f8d892018-04-09 12:14:00 -0700128 deviceService = get(DeviceService.class);
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700129
130 try {
131 mode = Mode.valueOf(modeStr);
132 } catch (IllegalArgumentException e) {
133 printlog("{} is not a valid Mode, pick one of {}",
134 modeStr,
135 Arrays.asList(Mode.values()),
136 e);
137 return;
138 }
139
140 // effectively configuration context
Yuta HIGUCHId0f8d892018-04-09 12:14:00 -0700141 List<CharSequence> nodes = new ArrayList<>();
142
143 // driver selection with fallback to plain OpenConfig
144 DeviceId did = Optional.ofNullable(uri)
145 .map(DeviceId::deviceId)
146 .orElse(null);
147 ConfigurableTransceiver transceiver =
148 Optional.ofNullable(did)
149 .map(deviceService::getDevice)
150 .filter(device -> device.is(ConfigurableTransceiver.class))
151 .map(device -> device.as(ConfigurableTransceiver.class))
152 .orElseGet(() -> new PlainTransceiver());
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700153
154 switch (mode) {
155 case PRECONF_TRANSCEIVER:
Yuta HIGUCHId0f8d892018-04-09 12:14:00 -0700156 // note: these doesn't support driver
157 nodes.addAll(transform(Transceiver.preconf(componentName)));
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700158 break;
159
160 case ENABLE_TRANSCEIVER:
Yuta HIGUCHI01c66eb2018-05-22 12:12:35 -0700161 if (cltPortNo != null && linePortNo != null) {
162 nodes.addAll(transceiver.enable(PortNumber.portNumber(cltPortNo),
163 PortNumber.portNumber(linePortNo),
164 true));
Yuta HIGUCHI9ee86642018-05-15 15:25:00 -0700165 } else {
166 nodes.addAll(transceiver.enable(componentName, true));
167 }
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700168 break;
169
170 case DISABLE_TRANSCEIVER:
Yuta HIGUCHI01c66eb2018-05-22 12:12:35 -0700171 if (cltPortNo != null && linePortNo != null) {
172 nodes.addAll(transceiver.enable(PortNumber.portNumber(cltPortNo),
173 PortNumber.portNumber(linePortNo),
174 false));
Yuta HIGUCHI9ee86642018-05-15 15:25:00 -0700175 } else {
176 nodes.addAll(transceiver.enable(componentName, false));
177 }
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700178 break;
179
180 case PRECONF_OPTICAL_CHANNEL:
Yuta HIGUCHId0f8d892018-04-09 12:14:00 -0700181 // note: these doesn't support driver
182 nodes.addAll(transform(OpticalChannel.preconf(componentName)));
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700183 break;
184
185 default:
186 printlog("Mode {} not supported yet", mode);
187 break;
188 }
189
190 // Do something about it.
191 doTheMagic(nodes);
192 }
193
Yuta HIGUCHId0f8d892018-04-09 12:14:00 -0700194 void doTheMagic(List<CharSequence> nodes) {
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700195
196 Document doc;
197 try {
198 doc = DocumentBuilderFactory.newInstance()
199 .newDocumentBuilder().newDocument();
200 } catch (ParserConfigurationException e) {
201 printlog("Unexpected error", e);
Ray Milkey6fc64c12018-04-12 09:35:33 -0700202 throw new IllegalStateException(e);
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700203 }
204
205 // netconf rpc boilerplate part without message-id
206 Element rpc = doc.createElementNS("urn:ietf:params:xml:ns:netconf:base:1.0", "rpc");
207 doc.appendChild(rpc);
208 Element editConfig = doc.createElement("edit-config");
209 rpc.appendChild(editConfig);
210 Element target = doc.createElement("target");
211 editConfig.appendChild(target);
212 target.appendChild(doc.createElement("running"));
213
214 Element config = doc.createElement("config");
215 config.setAttributeNS("http://www.w3.org/2000/xmlns/",
216 "xmlns:xc",
217 "urn:ietf:params:xml:ns:netconf:base:1.0");
218 editConfig.appendChild(config);
219
220
Yuta HIGUCHId0f8d892018-04-09 12:14:00 -0700221 for (CharSequence node : nodes) {
222 Document ldoc = toDocument(CharSource.wrap(node));
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700223 Element cfgRoot = ldoc.getDocumentElement();
224
225 // is everything as merge, ok?
226 cfgRoot.setAttribute("xc:operation", "merge");
227
228 // move (or copy) node to another Document
229 config.appendChild(Optional.ofNullable(doc.adoptNode(cfgRoot))
Yuta HIGUCHId0f8d892018-04-09 12:14:00 -0700230 .orElseGet(() -> doc.importNode(cfgRoot, true)));
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700231
232 // don't have good use for JSON for now
Yuta HIGUCHId0f8d892018-04-09 12:14:00 -0700233 //JsonNode json = toJsonNode(toJsonCompositeStream(toCompositeData(toResourceData(resourceId, node))));
234 //printlog("JSON:\n{}", toCharSequence(json));
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700235 }
236
237 printlog("XML:\n{}", XmlString.prettifyXml(toCharSequence(doc)));
238
239 // TODO if deviceId is given send it out to the device
240 if (uri != null) {
241 DeviceId deviceId = DeviceId.deviceId(uri);
242 NetconfController ctr = get(NetconfController.class);
243 Optional.ofNullable(ctr.getNetconfDevice(deviceId))
244 .map(NetconfDevice::getSession)
245 .ifPresent(session -> {
246 try {
247 session.rpc(toCharSequence(doc, false).toString()).join();
248 } catch (NetconfException e) {
249 log.error("Exception thrown", e);
250 }
251 });
252 }
253 }
254}