blob: c393487f25d42c46a3a3c0f9457b6a4003628454 [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
34import org.apache.karaf.shell.commands.Argument;
35import org.apache.karaf.shell.commands.Command;
36import org.apache.karaf.shell.commands.Option;
37import 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
90 @Option(name = "--portNo", description = "PortNumber to send configuration to",
91 required = false)
92 String portNo = null;
93
94
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -070095 // TODO add completer for this?
96 @Option(name = "--component",
97 description = "Component name",
98 required = false, multiValued = false)
99 private String componentName = "TRANSCEIVER_1_1_4_1";
100
101
102 // OSGi Services to be filled in at the beginning.
103 private DynamicConfigService dcs;
Yuta HIGUCHId0f8d892018-04-09 12:14:00 -0700104 private DeviceService deviceService;
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700105
106
107 void printlog(String format, Object... objs) {
108 print(format.replaceAll(Pattern.quote("{}"), "%s"), objs);
109 log.info(format, objs);
110 }
111
Yuta HIGUCHId0f8d892018-04-09 12:14:00 -0700112 List<CharSequence> transform(List<DataNode> input) {
113 ResourceId empty = ResourceId.builder().build();
114 return Lists.transform(input,
115 node -> toCharSequence(toXmlCompositeStream(toCompositeData(toResourceData(empty, node)))));
116 }
117
118
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700119 @Override
120 protected void execute() {
121 dcs = get(DynamicConfigService.class);
Yuta HIGUCHId0f8d892018-04-09 12:14:00 -0700122 deviceService = get(DeviceService.class);
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700123
124 try {
125 mode = Mode.valueOf(modeStr);
126 } catch (IllegalArgumentException e) {
127 printlog("{} is not a valid Mode, pick one of {}",
128 modeStr,
129 Arrays.asList(Mode.values()),
130 e);
131 return;
132 }
133
134 // effectively configuration context
Yuta HIGUCHId0f8d892018-04-09 12:14:00 -0700135 List<CharSequence> nodes = new ArrayList<>();
136
137 // driver selection with fallback to plain OpenConfig
138 DeviceId did = Optional.ofNullable(uri)
139 .map(DeviceId::deviceId)
140 .orElse(null);
141 ConfigurableTransceiver transceiver =
142 Optional.ofNullable(did)
143 .map(deviceService::getDevice)
144 .filter(device -> device.is(ConfigurableTransceiver.class))
145 .map(device -> device.as(ConfigurableTransceiver.class))
146 .orElseGet(() -> new PlainTransceiver());
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700147
148 switch (mode) {
149 case PRECONF_TRANSCEIVER:
Yuta HIGUCHId0f8d892018-04-09 12:14:00 -0700150 // note: these doesn't support driver
151 nodes.addAll(transform(Transceiver.preconf(componentName)));
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700152 break;
153
154 case ENABLE_TRANSCEIVER:
Yuta HIGUCHI9ee86642018-05-15 15:25:00 -0700155 if (portNo != null) {
156 nodes.addAll(transceiver.enable(PortNumber.portNumber(portNo), true));
157 } else {
158 nodes.addAll(transceiver.enable(componentName, true));
159 }
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700160 break;
161
162 case DISABLE_TRANSCEIVER:
Yuta HIGUCHI9ee86642018-05-15 15:25:00 -0700163 if (portNo != null) {
164 nodes.addAll(transceiver.enable(PortNumber.portNumber(portNo), false));
165 } else {
166 nodes.addAll(transceiver.enable(componentName, false));
167 }
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700168 break;
169
170 case PRECONF_OPTICAL_CHANNEL:
Yuta HIGUCHId0f8d892018-04-09 12:14:00 -0700171 // note: these doesn't support driver
172 nodes.addAll(transform(OpticalChannel.preconf(componentName)));
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700173 break;
174
175 default:
176 printlog("Mode {} not supported yet", mode);
177 break;
178 }
179
180 // Do something about it.
181 doTheMagic(nodes);
182 }
183
Yuta HIGUCHId0f8d892018-04-09 12:14:00 -0700184 void doTheMagic(List<CharSequence> nodes) {
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700185
186 Document doc;
187 try {
188 doc = DocumentBuilderFactory.newInstance()
189 .newDocumentBuilder().newDocument();
190 } catch (ParserConfigurationException e) {
191 printlog("Unexpected error", e);
Ray Milkey6fc64c12018-04-12 09:35:33 -0700192 throw new IllegalStateException(e);
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700193 }
194
195 // netconf rpc boilerplate part without message-id
196 Element rpc = doc.createElementNS("urn:ietf:params:xml:ns:netconf:base:1.0", "rpc");
197 doc.appendChild(rpc);
198 Element editConfig = doc.createElement("edit-config");
199 rpc.appendChild(editConfig);
200 Element target = doc.createElement("target");
201 editConfig.appendChild(target);
202 target.appendChild(doc.createElement("running"));
203
204 Element config = doc.createElement("config");
205 config.setAttributeNS("http://www.w3.org/2000/xmlns/",
206 "xmlns:xc",
207 "urn:ietf:params:xml:ns:netconf:base:1.0");
208 editConfig.appendChild(config);
209
210
Yuta HIGUCHId0f8d892018-04-09 12:14:00 -0700211 for (CharSequence node : nodes) {
212 Document ldoc = toDocument(CharSource.wrap(node));
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700213 Element cfgRoot = ldoc.getDocumentElement();
214
215 // is everything as merge, ok?
216 cfgRoot.setAttribute("xc:operation", "merge");
217
218 // move (or copy) node to another Document
219 config.appendChild(Optional.ofNullable(doc.adoptNode(cfgRoot))
Yuta HIGUCHId0f8d892018-04-09 12:14:00 -0700220 .orElseGet(() -> doc.importNode(cfgRoot, true)));
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700221
222 // don't have good use for JSON for now
Yuta HIGUCHId0f8d892018-04-09 12:14:00 -0700223 //JsonNode json = toJsonNode(toJsonCompositeStream(toCompositeData(toResourceData(resourceId, node))));
224 //printlog("JSON:\n{}", toCharSequence(json));
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700225 }
226
227 printlog("XML:\n{}", XmlString.prettifyXml(toCharSequence(doc)));
228
229 // TODO if deviceId is given send it out to the device
230 if (uri != null) {
231 DeviceId deviceId = DeviceId.deviceId(uri);
232 NetconfController ctr = get(NetconfController.class);
233 Optional.ofNullable(ctr.getNetconfDevice(deviceId))
234 .map(NetconfDevice::getSession)
235 .ifPresent(session -> {
236 try {
237 session.rpc(toCharSequence(doc, false).toString()).join();
238 } catch (NetconfException e) {
239 log.error("Exception thrown", e);
240 }
241 });
242 }
243 }
244}