blob: e61a2afae883db7716097e3ca0cc8c01ad6beb24 [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;
36import org.apache.karaf.shell.api.action.Option;
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -070037import org.onlab.util.XmlString;
38import org.onosproject.cli.AbstractShellCommand;
39import org.onosproject.cli.net.DeviceIdCompleter;
Yuta HIGUCHI9ee86642018-05-15 15:25:00 -070040import org.onosproject.cli.net.PortNumberCompleter;
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -070041import org.onosproject.config.DynamicConfigService;
42import org.onosproject.net.DeviceId;
Yuta HIGUCHI9ee86642018-05-15 15:25:00 -070043import org.onosproject.net.PortNumber;
Yuta HIGUCHId0f8d892018-04-09 12:14:00 -070044import org.onosproject.net.device.DeviceService;
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -070045import org.onosproject.netconf.NetconfController;
46import org.onosproject.netconf.NetconfDevice;
47import org.onosproject.netconf.NetconfException;
Yuta HIGUCHId0f8d892018-04-09 12:14:00 -070048import org.onosproject.odtn.behaviour.ConfigurableTransceiver;
49import org.onosproject.odtn.behaviour.PlainTransceiver;
50import org.onosproject.odtn.utils.openconfig.OpticalChannel;
51import org.onosproject.odtn.utils.openconfig.Transceiver;
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -070052import org.onosproject.yang.model.DataNode;
53import org.onosproject.yang.model.ResourceId;
54import org.slf4j.Logger;
55import org.w3c.dom.Document;
56import org.w3c.dom.Element;
57
Yuta HIGUCHId0f8d892018-04-09 12:14:00 -070058import com.google.common.collect.Lists;
59import com.google.common.io.CharSource;
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -070060
61
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -070062@Command(scope = "onos", name = "odtn-manual-test",
63 description = "ODTN manual test command")
64public class OdtnManualTestCommand extends AbstractShellCommand {
65
66 private static final Logger log = getLogger(OdtnManualTestCommand.class);
67
68 public static enum Mode {
69 ENABLE_TRANSCEIVER,
70 DISABLE_TRANSCEIVER,
71 PRECONF_TRANSCEIVER,
72 PRECONF_OPTICAL_CHANNEL,
73 }
74
75 ModeCompleter modeCompleter;
76 @Argument(index = 0, name = "mode", description = "one of Mode see source",
77 required = true)
78 String modeStr = Mode.ENABLE_TRANSCEIVER.name();
79 Mode mode;
80
81 // injecting dependency for OSGi package import generation purpose
82 DeviceIdCompleter uriCompleter;
83 @Option(name = "--deviceId", description = "Device ID URI to send configuration to",
84 required = false)
85 String uri = null;
86
Yuta HIGUCHI9ee86642018-05-15 15:25:00 -070087 // injecting dependency for OSGi package import generation purpose
88 PortNumberCompleter portNoCompleter;
89 // 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)
Yuta HIGUCHI01c66eb2018-05-22 12:12:35 -070092 String cltPortNo = null;
93
94 @Option(name = "--linePortNo", description = "Line-side PortNumber to send configuration to",
95 required = false)
96 String linePortNo = null;
Yuta HIGUCHI9ee86642018-05-15 15:25:00 -070097
98
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -070099 // TODO add completer for this?
100 @Option(name = "--component",
101 description = "Component name",
102 required = false, multiValued = false)
103 private String componentName = "TRANSCEIVER_1_1_4_1";
104
105
106 // OSGi Services to be filled in at the beginning.
107 private DynamicConfigService dcs;
Yuta HIGUCHId0f8d892018-04-09 12:14:00 -0700108 private DeviceService deviceService;
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700109
110
111 void printlog(String format, Object... objs) {
112 print(format.replaceAll(Pattern.quote("{}"), "%s"), objs);
113 log.info(format, objs);
114 }
115
Yuta HIGUCHId0f8d892018-04-09 12:14:00 -0700116 List<CharSequence> transform(List<DataNode> input) {
117 ResourceId empty = ResourceId.builder().build();
118 return Lists.transform(input,
119 node -> toCharSequence(toXmlCompositeStream(toCompositeData(toResourceData(empty, node)))));
120 }
121
122
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700123 @Override
Ray Milkey86ad7bb2018-09-27 12:32:28 -0700124 protected void doExecute() {
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700125 dcs = get(DynamicConfigService.class);
Yuta HIGUCHId0f8d892018-04-09 12:14:00 -0700126 deviceService = get(DeviceService.class);
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700127
128 try {
129 mode = Mode.valueOf(modeStr);
130 } catch (IllegalArgumentException e) {
131 printlog("{} is not a valid Mode, pick one of {}",
132 modeStr,
133 Arrays.asList(Mode.values()),
134 e);
135 return;
136 }
137
138 // effectively configuration context
Yuta HIGUCHId0f8d892018-04-09 12:14:00 -0700139 List<CharSequence> nodes = new ArrayList<>();
140
141 // driver selection with fallback to plain OpenConfig
142 DeviceId did = Optional.ofNullable(uri)
143 .map(DeviceId::deviceId)
144 .orElse(null);
145 ConfigurableTransceiver transceiver =
146 Optional.ofNullable(did)
147 .map(deviceService::getDevice)
148 .filter(device -> device.is(ConfigurableTransceiver.class))
149 .map(device -> device.as(ConfigurableTransceiver.class))
150 .orElseGet(() -> new PlainTransceiver());
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700151
152 switch (mode) {
153 case PRECONF_TRANSCEIVER:
Yuta HIGUCHId0f8d892018-04-09 12:14:00 -0700154 // note: these doesn't support driver
155 nodes.addAll(transform(Transceiver.preconf(componentName)));
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700156 break;
157
158 case ENABLE_TRANSCEIVER:
Yuta HIGUCHI01c66eb2018-05-22 12:12:35 -0700159 if (cltPortNo != null && linePortNo != null) {
160 nodes.addAll(transceiver.enable(PortNumber.portNumber(cltPortNo),
161 PortNumber.portNumber(linePortNo),
162 true));
Yuta HIGUCHI9ee86642018-05-15 15:25:00 -0700163 } else {
164 nodes.addAll(transceiver.enable(componentName, true));
165 }
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700166 break;
167
168 case DISABLE_TRANSCEIVER:
Yuta HIGUCHI01c66eb2018-05-22 12:12:35 -0700169 if (cltPortNo != null && linePortNo != null) {
170 nodes.addAll(transceiver.enable(PortNumber.portNumber(cltPortNo),
171 PortNumber.portNumber(linePortNo),
172 false));
Yuta HIGUCHI9ee86642018-05-15 15:25:00 -0700173 } else {
174 nodes.addAll(transceiver.enable(componentName, false));
175 }
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700176 break;
177
178 case PRECONF_OPTICAL_CHANNEL:
Yuta HIGUCHId0f8d892018-04-09 12:14:00 -0700179 // note: these doesn't support driver
180 nodes.addAll(transform(OpticalChannel.preconf(componentName)));
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700181 break;
182
183 default:
184 printlog("Mode {} not supported yet", mode);
185 break;
186 }
187
188 // Do something about it.
189 doTheMagic(nodes);
190 }
191
Yuta HIGUCHId0f8d892018-04-09 12:14:00 -0700192 void doTheMagic(List<CharSequence> nodes) {
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700193
194 Document doc;
195 try {
196 doc = DocumentBuilderFactory.newInstance()
197 .newDocumentBuilder().newDocument();
198 } catch (ParserConfigurationException e) {
199 printlog("Unexpected error", e);
Ray Milkey6fc64c12018-04-12 09:35:33 -0700200 throw new IllegalStateException(e);
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700201 }
202
203 // netconf rpc boilerplate part without message-id
204 Element rpc = doc.createElementNS("urn:ietf:params:xml:ns:netconf:base:1.0", "rpc");
205 doc.appendChild(rpc);
206 Element editConfig = doc.createElement("edit-config");
207 rpc.appendChild(editConfig);
208 Element target = doc.createElement("target");
209 editConfig.appendChild(target);
210 target.appendChild(doc.createElement("running"));
211
212 Element config = doc.createElement("config");
213 config.setAttributeNS("http://www.w3.org/2000/xmlns/",
214 "xmlns:xc",
215 "urn:ietf:params:xml:ns:netconf:base:1.0");
216 editConfig.appendChild(config);
217
218
Yuta HIGUCHId0f8d892018-04-09 12:14:00 -0700219 for (CharSequence node : nodes) {
220 Document ldoc = toDocument(CharSource.wrap(node));
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700221 Element cfgRoot = ldoc.getDocumentElement();
222
223 // is everything as merge, ok?
224 cfgRoot.setAttribute("xc:operation", "merge");
225
226 // move (or copy) node to another Document
227 config.appendChild(Optional.ofNullable(doc.adoptNode(cfgRoot))
Yuta HIGUCHId0f8d892018-04-09 12:14:00 -0700228 .orElseGet(() -> doc.importNode(cfgRoot, true)));
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700229
230 // don't have good use for JSON for now
Yuta HIGUCHId0f8d892018-04-09 12:14:00 -0700231 //JsonNode json = toJsonNode(toJsonCompositeStream(toCompositeData(toResourceData(resourceId, node))));
232 //printlog("JSON:\n{}", toCharSequence(json));
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700233 }
234
235 printlog("XML:\n{}", XmlString.prettifyXml(toCharSequence(doc)));
236
237 // TODO if deviceId is given send it out to the device
238 if (uri != null) {
239 DeviceId deviceId = DeviceId.deviceId(uri);
240 NetconfController ctr = get(NetconfController.class);
241 Optional.ofNullable(ctr.getNetconfDevice(deviceId))
242 .map(NetconfDevice::getSession)
243 .ifPresent(session -> {
244 try {
245 session.rpc(toCharSequence(doc, false).toString()).join();
246 } catch (NetconfException e) {
247 log.error("Exception thrown", e);
248 }
249 });
250 }
251 }
252}