blob: 1f72785ac934c982b3bc078987ec1069ed4c5553 [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
60
61@Command(scope = "onos", name = "odtn-manual-test",
62 description = "ODTN manual test command")
63public class OdtnManualTestCommand extends AbstractShellCommand {
64
65 private static final Logger log = getLogger(OdtnManualTestCommand.class);
66
67 public static enum Mode {
68 ENABLE_TRANSCEIVER,
69 DISABLE_TRANSCEIVER,
70 PRECONF_TRANSCEIVER,
71 PRECONF_OPTICAL_CHANNEL,
72 }
73
74 ModeCompleter modeCompleter;
75 @Argument(index = 0, name = "mode", description = "one of Mode see source",
76 required = true)
77 String modeStr = Mode.ENABLE_TRANSCEIVER.name();
78 Mode mode;
79
80 // injecting dependency for OSGi package import generation purpose
81 DeviceIdCompleter uriCompleter;
82 @Option(name = "--deviceId", description = "Device ID URI to send configuration to",
83 required = false)
84 String uri = null;
85
86 // TODO add completer for this?
87 @Option(name = "--component",
88 description = "Component name",
89 required = false, multiValued = false)
90 private String componentName = "TRANSCEIVER_1_1_4_1";
91
92
93 // OSGi Services to be filled in at the beginning.
94 private DynamicConfigService dcs;
Yuta HIGUCHId0f8d892018-04-09 12:14:00 -070095 private DeviceService deviceService;
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -070096
97
98 void printlog(String format, Object... objs) {
99 print(format.replaceAll(Pattern.quote("{}"), "%s"), objs);
100 log.info(format, objs);
101 }
102
Yuta HIGUCHId0f8d892018-04-09 12:14:00 -0700103 List<CharSequence> transform(List<DataNode> input) {
104 ResourceId empty = ResourceId.builder().build();
105 return Lists.transform(input,
106 node -> toCharSequence(toXmlCompositeStream(toCompositeData(toResourceData(empty, node)))));
107 }
108
109
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700110 @Override
111 protected void execute() {
112 dcs = get(DynamicConfigService.class);
Yuta HIGUCHId0f8d892018-04-09 12:14:00 -0700113 deviceService = get(DeviceService.class);
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700114
115 try {
116 mode = Mode.valueOf(modeStr);
117 } catch (IllegalArgumentException e) {
118 printlog("{} is not a valid Mode, pick one of {}",
119 modeStr,
120 Arrays.asList(Mode.values()),
121 e);
122 return;
123 }
124
125 // effectively configuration context
Yuta HIGUCHId0f8d892018-04-09 12:14:00 -0700126 List<CharSequence> nodes = new ArrayList<>();
127
128 // driver selection with fallback to plain OpenConfig
129 DeviceId did = Optional.ofNullable(uri)
130 .map(DeviceId::deviceId)
131 .orElse(null);
132 ConfigurableTransceiver transceiver =
133 Optional.ofNullable(did)
134 .map(deviceService::getDevice)
135 .filter(device -> device.is(ConfigurableTransceiver.class))
136 .map(device -> device.as(ConfigurableTransceiver.class))
137 .orElseGet(() -> new PlainTransceiver());
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700138
139 switch (mode) {
140 case PRECONF_TRANSCEIVER:
Yuta HIGUCHId0f8d892018-04-09 12:14:00 -0700141 // note: these doesn't support driver
142 nodes.addAll(transform(Transceiver.preconf(componentName)));
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700143 break;
144
145 case ENABLE_TRANSCEIVER:
Yuta HIGUCHId0f8d892018-04-09 12:14:00 -0700146 nodes.addAll(transceiver.enable(componentName, true));
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700147 break;
148
149 case DISABLE_TRANSCEIVER:
Yuta HIGUCHId0f8d892018-04-09 12:14:00 -0700150 nodes.addAll(transceiver.enable(componentName, false));
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700151 break;
152
153 case PRECONF_OPTICAL_CHANNEL:
Yuta HIGUCHId0f8d892018-04-09 12:14:00 -0700154 // note: these doesn't support driver
155 nodes.addAll(transform(OpticalChannel.preconf(componentName)));
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700156 break;
157
158 default:
159 printlog("Mode {} not supported yet", mode);
160 break;
161 }
162
163 // Do something about it.
164 doTheMagic(nodes);
165 }
166
Yuta HIGUCHId0f8d892018-04-09 12:14:00 -0700167 void doTheMagic(List<CharSequence> nodes) {
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700168
169 Document doc;
170 try {
171 doc = DocumentBuilderFactory.newInstance()
172 .newDocumentBuilder().newDocument();
173 } catch (ParserConfigurationException e) {
174 printlog("Unexpected error", e);
Ray Milkey6fc64c12018-04-12 09:35:33 -0700175 throw new IllegalStateException(e);
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700176 }
177
178 // netconf rpc boilerplate part without message-id
179 Element rpc = doc.createElementNS("urn:ietf:params:xml:ns:netconf:base:1.0", "rpc");
180 doc.appendChild(rpc);
181 Element editConfig = doc.createElement("edit-config");
182 rpc.appendChild(editConfig);
183 Element target = doc.createElement("target");
184 editConfig.appendChild(target);
185 target.appendChild(doc.createElement("running"));
186
187 Element config = doc.createElement("config");
188 config.setAttributeNS("http://www.w3.org/2000/xmlns/",
189 "xmlns:xc",
190 "urn:ietf:params:xml:ns:netconf:base:1.0");
191 editConfig.appendChild(config);
192
193
Yuta HIGUCHId0f8d892018-04-09 12:14:00 -0700194 for (CharSequence node : nodes) {
195 Document ldoc = toDocument(CharSource.wrap(node));
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700196 Element cfgRoot = ldoc.getDocumentElement();
197
198 // is everything as merge, ok?
199 cfgRoot.setAttribute("xc:operation", "merge");
200
201 // move (or copy) node to another Document
202 config.appendChild(Optional.ofNullable(doc.adoptNode(cfgRoot))
Yuta HIGUCHId0f8d892018-04-09 12:14:00 -0700203 .orElseGet(() -> doc.importNode(cfgRoot, true)));
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700204
205 // don't have good use for JSON for now
Yuta HIGUCHId0f8d892018-04-09 12:14:00 -0700206 //JsonNode json = toJsonNode(toJsonCompositeStream(toCompositeData(toResourceData(resourceId, node))));
207 //printlog("JSON:\n{}", toCharSequence(json));
Yuta HIGUCHI48f4cb72018-03-29 20:30:56 -0700208 }
209
210 printlog("XML:\n{}", XmlString.prettifyXml(toCharSequence(doc)));
211
212 // TODO if deviceId is given send it out to the device
213 if (uri != null) {
214 DeviceId deviceId = DeviceId.deviceId(uri);
215 NetconfController ctr = get(NetconfController.class);
216 Optional.ofNullable(ctr.getNetconfDevice(deviceId))
217 .map(NetconfDevice::getSession)
218 .ifPresent(session -> {
219 try {
220 session.rpc(toCharSequence(doc, false).toString()).join();
221 } catch (NetconfException e) {
222 log.error("Exception thrown", e);
223 }
224 });
225 }
226 }
227}