blob: b31ee83dbdec540623738b8a87e26a147a03ff51 [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;
40import org.onosproject.config.DynamicConfigService;
41import org.onosproject.net.DeviceId;
Yuta HIGUCHId0f8d892018-04-09 12:14:00 -070042import org.onosproject.net.device.DeviceService;
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -070043import org.onosproject.netconf.NetconfController;
44import org.onosproject.netconf.NetconfDevice;
45import org.onosproject.netconf.NetconfException;
Yuta HIGUCHId0f8d892018-04-09 12:14:00 -070046import org.onosproject.odtn.behaviour.ConfigurableTransceiver;
47import org.onosproject.odtn.behaviour.PlainTransceiver;
48import org.onosproject.odtn.utils.openconfig.OpticalChannel;
49import org.onosproject.odtn.utils.openconfig.Transceiver;
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -070050import org.onosproject.yang.model.DataNode;
51import org.onosproject.yang.model.ResourceId;
52import org.slf4j.Logger;
53import org.w3c.dom.Document;
54import org.w3c.dom.Element;
55
Yuta HIGUCHId0f8d892018-04-09 12:14:00 -070056import com.google.common.collect.Lists;
57import com.google.common.io.CharSource;
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -070058
59
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -070060@Command(scope = "onos", name = "odtn-manual-test",
61 description = "ODTN manual test command")
62public class OdtnManualTestCommand extends AbstractShellCommand {
63
64 private static final Logger log = getLogger(OdtnManualTestCommand.class);
65
66 public static enum Mode {
67 ENABLE_TRANSCEIVER,
68 DISABLE_TRANSCEIVER,
69 PRECONF_TRANSCEIVER,
70 PRECONF_OPTICAL_CHANNEL,
71 }
72
73 ModeCompleter modeCompleter;
74 @Argument(index = 0, name = "mode", description = "one of Mode see source",
75 required = true)
76 String modeStr = Mode.ENABLE_TRANSCEIVER.name();
77 Mode mode;
78
79 // injecting dependency for OSGi package import generation purpose
80 DeviceIdCompleter uriCompleter;
81 @Option(name = "--deviceId", description = "Device ID URI to send configuration to",
82 required = false)
83 String uri = null;
84
85 // TODO add completer for this?
86 @Option(name = "--component",
87 description = "Component name",
88 required = false, multiValued = false)
89 private String componentName = "TRANSCEIVER_1_1_4_1";
90
91
92 // OSGi Services to be filled in at the beginning.
93 private DynamicConfigService dcs;
Yuta HIGUCHId0f8d892018-04-09 12:14:00 -070094 private DeviceService deviceService;
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -070095
96
97 void printlog(String format, Object... objs) {
98 print(format.replaceAll(Pattern.quote("{}"), "%s"), objs);
99 log.info(format, objs);
100 }
101
Yuta HIGUCHId0f8d892018-04-09 12:14:00 -0700102 List<CharSequence> transform(List<DataNode> input) {
103 ResourceId empty = ResourceId.builder().build();
104 return Lists.transform(input,
105 node -> toCharSequence(toXmlCompositeStream(toCompositeData(toResourceData(empty, node)))));
106 }
107
108
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700109 @Override
110 protected void execute() {
111 dcs = get(DynamicConfigService.class);
Yuta HIGUCHId0f8d892018-04-09 12:14:00 -0700112 deviceService = get(DeviceService.class);
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700113
114 try {
115 mode = Mode.valueOf(modeStr);
116 } catch (IllegalArgumentException e) {
117 printlog("{} is not a valid Mode, pick one of {}",
118 modeStr,
119 Arrays.asList(Mode.values()),
120 e);
121 return;
122 }
123
124 // effectively configuration context
Yuta HIGUCHId0f8d892018-04-09 12:14:00 -0700125 List<CharSequence> nodes = new ArrayList<>();
126
127 // driver selection with fallback to plain OpenConfig
128 DeviceId did = Optional.ofNullable(uri)
129 .map(DeviceId::deviceId)
130 .orElse(null);
131 ConfigurableTransceiver transceiver =
132 Optional.ofNullable(did)
133 .map(deviceService::getDevice)
134 .filter(device -> device.is(ConfigurableTransceiver.class))
135 .map(device -> device.as(ConfigurableTransceiver.class))
136 .orElseGet(() -> new PlainTransceiver());
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700137
138 switch (mode) {
139 case PRECONF_TRANSCEIVER:
Yuta HIGUCHId0f8d892018-04-09 12:14:00 -0700140 // note: these doesn't support driver
141 nodes.addAll(transform(Transceiver.preconf(componentName)));
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700142 break;
143
144 case ENABLE_TRANSCEIVER:
Yuta HIGUCHId0f8d892018-04-09 12:14:00 -0700145 nodes.addAll(transceiver.enable(componentName, true));
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700146 break;
147
148 case DISABLE_TRANSCEIVER:
Yuta HIGUCHId0f8d892018-04-09 12:14:00 -0700149 nodes.addAll(transceiver.enable(componentName, false));
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700150 break;
151
152 case PRECONF_OPTICAL_CHANNEL:
Yuta HIGUCHId0f8d892018-04-09 12:14:00 -0700153 // note: these doesn't support driver
154 nodes.addAll(transform(OpticalChannel.preconf(componentName)));
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700155 break;
156
157 default:
158 printlog("Mode {} not supported yet", mode);
159 break;
160 }
161
162 // Do something about it.
163 doTheMagic(nodes);
164 }
165
Yuta HIGUCHId0f8d892018-04-09 12:14:00 -0700166 void doTheMagic(List<CharSequence> nodes) {
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700167
168 Document doc;
169 try {
170 doc = DocumentBuilderFactory.newInstance()
171 .newDocumentBuilder().newDocument();
172 } catch (ParserConfigurationException e) {
173 printlog("Unexpected error", e);
Ray Milkey6fc64c12018-04-12 09:35:33 -0700174 throw new IllegalStateException(e);
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700175 }
176
177 // netconf rpc boilerplate part without message-id
178 Element rpc = doc.createElementNS("urn:ietf:params:xml:ns:netconf:base:1.0", "rpc");
179 doc.appendChild(rpc);
180 Element editConfig = doc.createElement("edit-config");
181 rpc.appendChild(editConfig);
182 Element target = doc.createElement("target");
183 editConfig.appendChild(target);
184 target.appendChild(doc.createElement("running"));
185
186 Element config = doc.createElement("config");
187 config.setAttributeNS("http://www.w3.org/2000/xmlns/",
188 "xmlns:xc",
189 "urn:ietf:params:xml:ns:netconf:base:1.0");
190 editConfig.appendChild(config);
191
192
Yuta HIGUCHId0f8d892018-04-09 12:14:00 -0700193 for (CharSequence node : nodes) {
194 Document ldoc = toDocument(CharSource.wrap(node));
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700195 Element cfgRoot = ldoc.getDocumentElement();
196
197 // is everything as merge, ok?
198 cfgRoot.setAttribute("xc:operation", "merge");
199
200 // move (or copy) node to another Document
201 config.appendChild(Optional.ofNullable(doc.adoptNode(cfgRoot))
Yuta HIGUCHId0f8d892018-04-09 12:14:00 -0700202 .orElseGet(() -> doc.importNode(cfgRoot, true)));
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700203
204 // don't have good use for JSON for now
Yuta HIGUCHId0f8d892018-04-09 12:14:00 -0700205 //JsonNode json = toJsonNode(toJsonCompositeStream(toCompositeData(toResourceData(resourceId, node))));
206 //printlog("JSON:\n{}", toCharSequence(json));
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700207 }
208
209 printlog("XML:\n{}", XmlString.prettifyXml(toCharSequence(doc)));
210
211 // TODO if deviceId is given send it out to the device
212 if (uri != null) {
213 DeviceId deviceId = DeviceId.deviceId(uri);
214 NetconfController ctr = get(NetconfController.class);
215 Optional.ofNullable(ctr.getNetconfDevice(deviceId))
216 .map(NetconfDevice::getSession)
217 .ifPresent(session -> {
218 try {
219 session.rpc(toCharSequence(doc, false).toString()).join();
220 } catch (NetconfException e) {
221 log.error("Exception thrown", e);
222 }
223 });
224 }
225 }
226}