blob: 581f01b8c88d695a7c026501729ed5dd52a3d6cc [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;
Ray Milkey7a2dee52018-09-28 10:58:28 -070037import org.apache.karaf.shell.api.action.lifecycle.Service;
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -070038import org.onlab.util.XmlString;
39import org.onosproject.cli.AbstractShellCommand;
40import org.onosproject.cli.net.DeviceIdCompleter;
Yuta HIGUCHI9ee86642018-05-15 15:25:00 -070041import org.onosproject.cli.net.PortNumberCompleter;
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -070042import org.onosproject.config.DynamicConfigService;
43import org.onosproject.net.DeviceId;
Yuta HIGUCHI9ee86642018-05-15 15:25:00 -070044import org.onosproject.net.PortNumber;
Yuta HIGUCHId0f8d892018-04-09 12:14:00 -070045import org.onosproject.net.device.DeviceService;
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -070046import org.onosproject.netconf.NetconfController;
47import org.onosproject.netconf.NetconfDevice;
48import org.onosproject.netconf.NetconfException;
Yuta HIGUCHId0f8d892018-04-09 12:14:00 -070049import org.onosproject.odtn.behaviour.ConfigurableTransceiver;
50import org.onosproject.odtn.behaviour.PlainTransceiver;
51import org.onosproject.odtn.utils.openconfig.OpticalChannel;
52import org.onosproject.odtn.utils.openconfig.Transceiver;
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -070053import org.onosproject.yang.model.DataNode;
54import org.onosproject.yang.model.ResourceId;
55import org.slf4j.Logger;
56import org.w3c.dom.Document;
57import org.w3c.dom.Element;
58
Yuta HIGUCHId0f8d892018-04-09 12:14:00 -070059import com.google.common.collect.Lists;
60import com.google.common.io.CharSource;
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -070061
Ray Milkey7a2dee52018-09-28 10:58:28 -070062@Service
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -070063@Command(scope = "onos", name = "odtn-manual-test",
64 description = "ODTN manual test command")
65public class OdtnManualTestCommand extends AbstractShellCommand {
66
67 private static final Logger log = getLogger(OdtnManualTestCommand.class);
68
69 public static enum Mode {
70 ENABLE_TRANSCEIVER,
71 DISABLE_TRANSCEIVER,
72 PRECONF_TRANSCEIVER,
73 PRECONF_OPTICAL_CHANNEL,
74 }
75
76 ModeCompleter modeCompleter;
77 @Argument(index = 0, name = "mode", description = "one of Mode see source",
78 required = true)
79 String modeStr = Mode.ENABLE_TRANSCEIVER.name();
80 Mode mode;
81
82 // injecting dependency for OSGi package import generation purpose
83 DeviceIdCompleter uriCompleter;
84 @Option(name = "--deviceId", description = "Device ID URI to send configuration to",
85 required = false)
86 String uri = null;
87
Yuta HIGUCHI9ee86642018-05-15 15:25:00 -070088 // injecting dependency for OSGi package import generation purpose
89 PortNumberCompleter portNoCompleter;
90 // Note: this will required Port information in device subystem
Yuta HIGUCHI01c66eb2018-05-22 12:12:35 -070091 @Option(name = "--cltPortNo", description = "Client-side PortNumber to send configuration to",
Yuta HIGUCHI9ee86642018-05-15 15:25:00 -070092 required = false)
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)
97 String linePortNo = null;
Yuta HIGUCHI9ee86642018-05-15 15:25:00 -070098
99
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700100 // TODO add completer for this?
101 @Option(name = "--component",
102 description = "Component name",
103 required = false, multiValued = false)
104 private String componentName = "TRANSCEIVER_1_1_4_1";
105
106
107 // OSGi Services to be filled in at the beginning.
108 private DynamicConfigService dcs;
Yuta HIGUCHId0f8d892018-04-09 12:14:00 -0700109 private DeviceService deviceService;
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700110
111
112 void printlog(String format, Object... objs) {
113 print(format.replaceAll(Pattern.quote("{}"), "%s"), objs);
114 log.info(format, objs);
115 }
116
Yuta HIGUCHId0f8d892018-04-09 12:14:00 -0700117 List<CharSequence> transform(List<DataNode> input) {
118 ResourceId empty = ResourceId.builder().build();
119 return Lists.transform(input,
120 node -> toCharSequence(toXmlCompositeStream(toCompositeData(toResourceData(empty, node)))));
121 }
122
123
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700124 @Override
Ray Milkey86ad7bb2018-09-27 12:32:28 -0700125 protected void doExecute() {
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700126 dcs = get(DynamicConfigService.class);
Yuta HIGUCHId0f8d892018-04-09 12:14:00 -0700127 deviceService = get(DeviceService.class);
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700128
129 try {
130 mode = Mode.valueOf(modeStr);
131 } catch (IllegalArgumentException e) {
132 printlog("{} is not a valid Mode, pick one of {}",
133 modeStr,
134 Arrays.asList(Mode.values()),
135 e);
136 return;
137 }
138
139 // effectively configuration context
Yuta HIGUCHId0f8d892018-04-09 12:14:00 -0700140 List<CharSequence> nodes = new ArrayList<>();
141
142 // driver selection with fallback to plain OpenConfig
143 DeviceId did = Optional.ofNullable(uri)
144 .map(DeviceId::deviceId)
145 .orElse(null);
146 ConfigurableTransceiver transceiver =
147 Optional.ofNullable(did)
148 .map(deviceService::getDevice)
149 .filter(device -> device.is(ConfigurableTransceiver.class))
150 .map(device -> device.as(ConfigurableTransceiver.class))
151 .orElseGet(() -> new PlainTransceiver());
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700152
153 switch (mode) {
154 case PRECONF_TRANSCEIVER:
Yuta HIGUCHId0f8d892018-04-09 12:14:00 -0700155 // note: these doesn't support driver
156 nodes.addAll(transform(Transceiver.preconf(componentName)));
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700157 break;
158
159 case ENABLE_TRANSCEIVER:
Yuta HIGUCHI01c66eb2018-05-22 12:12:35 -0700160 if (cltPortNo != null && linePortNo != null) {
161 nodes.addAll(transceiver.enable(PortNumber.portNumber(cltPortNo),
162 PortNumber.portNumber(linePortNo),
163 true));
Yuta HIGUCHI9ee86642018-05-15 15:25:00 -0700164 } else {
165 nodes.addAll(transceiver.enable(componentName, true));
166 }
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700167 break;
168
169 case DISABLE_TRANSCEIVER:
Yuta HIGUCHI01c66eb2018-05-22 12:12:35 -0700170 if (cltPortNo != null && linePortNo != null) {
171 nodes.addAll(transceiver.enable(PortNumber.portNumber(cltPortNo),
172 PortNumber.portNumber(linePortNo),
173 false));
Yuta HIGUCHI9ee86642018-05-15 15:25:00 -0700174 } else {
175 nodes.addAll(transceiver.enable(componentName, false));
176 }
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700177 break;
178
179 case PRECONF_OPTICAL_CHANNEL:
Yuta HIGUCHId0f8d892018-04-09 12:14:00 -0700180 // note: these doesn't support driver
181 nodes.addAll(transform(OpticalChannel.preconf(componentName)));
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700182 break;
183
184 default:
185 printlog("Mode {} not supported yet", mode);
186 break;
187 }
188
189 // Do something about it.
190 doTheMagic(nodes);
191 }
192
Yuta HIGUCHId0f8d892018-04-09 12:14:00 -0700193 void doTheMagic(List<CharSequence> nodes) {
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700194
195 Document doc;
196 try {
197 doc = DocumentBuilderFactory.newInstance()
198 .newDocumentBuilder().newDocument();
199 } catch (ParserConfigurationException e) {
200 printlog("Unexpected error", e);
Ray Milkey6fc64c12018-04-12 09:35:33 -0700201 throw new IllegalStateException(e);
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700202 }
203
204 // netconf rpc boilerplate part without message-id
205 Element rpc = doc.createElementNS("urn:ietf:params:xml:ns:netconf:base:1.0", "rpc");
206 doc.appendChild(rpc);
207 Element editConfig = doc.createElement("edit-config");
208 rpc.appendChild(editConfig);
209 Element target = doc.createElement("target");
210 editConfig.appendChild(target);
211 target.appendChild(doc.createElement("running"));
212
213 Element config = doc.createElement("config");
214 config.setAttributeNS("http://www.w3.org/2000/xmlns/",
215 "xmlns:xc",
216 "urn:ietf:params:xml:ns:netconf:base:1.0");
217 editConfig.appendChild(config);
218
219
Yuta HIGUCHId0f8d892018-04-09 12:14:00 -0700220 for (CharSequence node : nodes) {
221 Document ldoc = toDocument(CharSource.wrap(node));
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700222 Element cfgRoot = ldoc.getDocumentElement();
223
224 // is everything as merge, ok?
225 cfgRoot.setAttribute("xc:operation", "merge");
226
227 // move (or copy) node to another Document
228 config.appendChild(Optional.ofNullable(doc.adoptNode(cfgRoot))
Yuta HIGUCHId0f8d892018-04-09 12:14:00 -0700229 .orElseGet(() -> doc.importNode(cfgRoot, true)));
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700230
231 // don't have good use for JSON for now
Yuta HIGUCHId0f8d892018-04-09 12:14:00 -0700232 //JsonNode json = toJsonNode(toJsonCompositeStream(toCompositeData(toResourceData(resourceId, node))));
233 //printlog("JSON:\n{}", toCharSequence(json));
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700234 }
235
236 printlog("XML:\n{}", XmlString.prettifyXml(toCharSequence(doc)));
237
238 // TODO if deviceId is given send it out to the device
239 if (uri != null) {
240 DeviceId deviceId = DeviceId.deviceId(uri);
241 NetconfController ctr = get(NetconfController.class);
242 Optional.ofNullable(ctr.getNetconfDevice(deviceId))
243 .map(NetconfDevice::getSession)
244 .ifPresent(session -> {
245 try {
246 session.rpc(toCharSequence(doc, false).toString()).join();
247 } catch (NetconfException e) {
248 log.error("Exception thrown", e);
249 }
250 });
251 }
252 }
253}