jaegonkim | dcf7c82 | 2019-02-06 15:00:14 +0900 | [diff] [blame^] | 1 | /* |
| 2 | * Copyright 2019-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 | */ |
| 16 | package org.onosproject.ofoverlay.impl; |
| 17 | |
| 18 | import com.fasterxml.jackson.databind.JsonNode; |
| 19 | import com.fasterxml.jackson.databind.node.ArrayNode; |
| 20 | import com.fasterxml.jackson.databind.node.TextNode; |
| 21 | import com.google.common.collect.Lists; |
| 22 | import com.google.common.collect.Sets; |
| 23 | import org.onlab.packet.Ip4Address; |
| 24 | import org.onlab.packet.Ip6Address; |
| 25 | import org.onlab.packet.IpAddress; |
| 26 | import org.onlab.packet.TpPort; |
| 27 | import org.onosproject.cluster.ClusterService; |
| 28 | import org.onosproject.event.Event; |
| 29 | import org.onosproject.net.Device; |
| 30 | import org.onosproject.net.DeviceId; |
| 31 | import org.onosproject.net.Port; |
| 32 | import org.onosproject.net.behaviour.BridgeConfig; |
| 33 | import org.onosproject.net.behaviour.BridgeDescription; |
| 34 | import org.onosproject.net.behaviour.BridgeName; |
| 35 | import org.onosproject.net.behaviour.ControlProtocolVersion; |
| 36 | import org.onosproject.net.behaviour.ControllerInfo; |
| 37 | import org.onosproject.net.behaviour.DefaultBridgeDescription; |
| 38 | import org.onosproject.net.behaviour.DefaultTunnelDescription; |
| 39 | import org.onosproject.net.behaviour.InterfaceConfig; |
| 40 | import org.onosproject.net.behaviour.TunnelDescription; |
| 41 | import org.onosproject.net.behaviour.TunnelEndPoints; |
| 42 | import org.onosproject.net.behaviour.TunnelKeys; |
| 43 | import org.onosproject.net.device.DeviceAdminService; |
| 44 | import org.onosproject.net.device.DeviceEvent; |
| 45 | import org.onosproject.net.device.DeviceService; |
| 46 | import org.onosproject.net.driver.Behaviour; |
| 47 | import org.onosproject.net.driver.DriverHandler; |
| 48 | import org.onosproject.net.driver.DriverService; |
| 49 | import org.onosproject.ofoverlay.impl.util.NetworkAddress; |
| 50 | import org.onosproject.ofoverlay.impl.util.OvsDatapathType; |
| 51 | import org.onosproject.ofoverlay.impl.util.OvsVersion; |
| 52 | import org.onosproject.ofoverlay.impl.util.SshUtil; |
| 53 | import org.onosproject.ovsdb.controller.OvsdbClientService; |
| 54 | import org.onosproject.ovsdb.controller.OvsdbController; |
| 55 | import org.onosproject.ovsdb.controller.OvsdbNodeId; |
| 56 | import org.onosproject.workflow.api.AbstractWorklet; |
| 57 | import org.onosproject.workflow.api.JsonDataModel; |
| 58 | import org.onosproject.workflow.api.WorkflowContext; |
| 59 | import org.onosproject.workflow.api.WorkflowException; |
| 60 | import org.onosproject.workflow.model.accessinfo.SshAccessInfo; |
| 61 | import org.slf4j.Logger; |
| 62 | import org.slf4j.LoggerFactory; |
| 63 | |
| 64 | import java.util.ArrayList; |
| 65 | import java.util.Collection; |
| 66 | import java.util.Collections; |
| 67 | import java.util.List; |
| 68 | import java.util.Objects; |
| 69 | import java.util.Optional; |
| 70 | import java.util.stream.Collectors; |
| 71 | |
| 72 | import static org.onosproject.net.AnnotationKeys.PORT_NAME; |
| 73 | import static org.onosproject.workflow.api.CheckCondition.check; |
| 74 | |
| 75 | /** |
| 76 | * Class for defining OVS workflows. |
| 77 | */ |
| 78 | public final class Ovs { |
| 79 | |
| 80 | private static final Logger log = LoggerFactory.getLogger(Ovs.class); |
| 81 | |
| 82 | private static final String MODEL_MGMT_IP = "/mgmtIp"; |
| 83 | private static final String MODEL_OVSDB_PORT = "/ovsdbPort"; |
| 84 | private static final String MODEL_OVS_VERSION = "/ovsVersion"; |
| 85 | private static final String MODEL_OVS_DATAPATH_TYPE = "/ovsDatapathType"; |
| 86 | private static final String MODEL_SSH_ACCESSINFO = "/sshAccessInfo"; |
| 87 | private static final String MODEL_OF_DEVID_OVERLAY_BRIDGE = "/ofDevIdBrInt"; |
| 88 | private static final String MODEL_OF_DEVID_UNDERLAY_BRIDGE = "/ofDevIdBrPhy"; |
| 89 | private static final String MODEL_PHY_PORTS = "/physicalPorts"; |
| 90 | private static final String MODEL_VTEP_IP = "/vtepIp"; |
| 91 | |
| 92 | private static final String BRIDGE_OVERLAY = "br-int"; |
| 93 | private static final String BRIDGE_UNDERLAY = "br-phy"; |
| 94 | |
| 95 | private static final int DEVID_IDX_BRIDGE_OVERLAY = 0; |
| 96 | private static final int DEVID_IDX_BRIDGE_UNDERLAY_NOVA = 1; |
| 97 | |
| 98 | private static final ControlProtocolVersion BRIDGE_DEFAULT_OF_VERSION = ControlProtocolVersion.OF_1_3; |
| 99 | private static final int OPENFLOW_PORT = 6653; |
| 100 | private static final String OPENFLOW_CHANNEL_PROTO = "tcp"; |
| 101 | private static final String OVSDB_DEVICE_PREFIX = "ovsdb:"; |
| 102 | |
| 103 | private static final long TIMEOUT_DEVICE_CREATION_MS = 60000L; |
| 104 | private static final long TIMEOUT_PORT_ADDITION_MS = 120000L; |
| 105 | |
| 106 | |
| 107 | /** |
| 108 | * Utility class for OVS workflow. |
| 109 | */ |
| 110 | public static final class OvsUtil { |
| 111 | |
| 112 | private OvsUtil() { |
| 113 | |
| 114 | } |
| 115 | |
| 116 | private static final String OPENFLOW_DEVID_FORMAT = "of:%08x%08x"; |
| 117 | |
| 118 | /** |
| 119 | * Builds Open-flow device id with ip address, and index. |
| 120 | * @param addr ip address |
| 121 | * @param index index |
| 122 | * @return created device id |
| 123 | */ |
| 124 | public static DeviceId buildOfDeviceId(IpAddress addr, int index) { |
| 125 | if (addr.isIp4()) { |
| 126 | Ip4Address v4Addr = addr.getIp4Address(); |
| 127 | return DeviceId.deviceId(String.format(OPENFLOW_DEVID_FORMAT, v4Addr.toInt(), index)); |
| 128 | } else if (addr.isIp6()) { |
| 129 | Ip6Address v6Addr = addr.getIp6Address(); |
| 130 | return DeviceId.deviceId(String.format(OPENFLOW_DEVID_FORMAT, v6Addr.hashCode(), index)); |
| 131 | } else { |
| 132 | return DeviceId.deviceId(String.format(OPENFLOW_DEVID_FORMAT, addr.hashCode(), index)); |
| 133 | } |
| 134 | } |
| 135 | |
| 136 | /** |
| 137 | * Builds OVS data path type. |
| 138 | * @param strOvsDatapathType string ovs data path type |
| 139 | * @return ovs data path type |
| 140 | * @throws WorkflowException workflow exception |
| 141 | */ |
| 142 | public static final OvsDatapathType buildOvsDatapathType(String strOvsDatapathType) throws WorkflowException { |
| 143 | try { |
| 144 | return OvsDatapathType.valueOf(strOvsDatapathType.toUpperCase()); |
| 145 | } catch (IllegalArgumentException e) { |
| 146 | throw new WorkflowException(e); |
| 147 | } |
| 148 | } |
| 149 | |
| 150 | /** |
| 151 | * Gets OVSDB behavior. |
| 152 | * @param context workflow context |
| 153 | * @param mgmtIp management ip |
| 154 | * @param behaviourClass behavior class |
| 155 | * @param <T> behavior class |
| 156 | * @return OVSDB behavior |
| 157 | * @throws WorkflowException workflow exception |
| 158 | */ |
| 159 | public static final <T extends Behaviour> T getOvsdbBehaviour(WorkflowContext context, String mgmtIp, |
| 160 | Class<T> behaviourClass) throws WorkflowException { |
| 161 | |
| 162 | DriverService driverService = context.getService(DriverService.class); |
| 163 | |
| 164 | DeviceId devId = ovsdbDeviceId(mgmtIp); |
| 165 | DriverHandler handler = driverService.createHandler(devId); |
| 166 | if (Objects.isNull(handler)) { |
| 167 | throw new WorkflowException("Failed to get DriverHandler for " + devId); |
| 168 | } |
| 169 | T behaviour; |
| 170 | try { |
| 171 | behaviour = handler.behaviour(behaviourClass); |
| 172 | if (Objects.isNull(behaviour)) { |
| 173 | throw new WorkflowException("Failed to get " + behaviourClass + " for " + devId + "-" + handler); |
| 174 | } |
| 175 | } catch (IllegalArgumentException e) { |
| 176 | throw new WorkflowException("Failed to get " + behaviourClass + " for " + devId + "-" + handler); |
| 177 | } |
| 178 | return behaviour; |
| 179 | } |
| 180 | |
| 181 | /** |
| 182 | * Gets bridge description. |
| 183 | * @param bridgeConfig bridge config |
| 184 | * @param bridgeName bridge name |
| 185 | * @return bridge description optional |
| 186 | */ |
| 187 | public static final Optional<BridgeDescription> getBridgeDescription(BridgeConfig bridgeConfig, |
| 188 | String bridgeName) { |
| 189 | try { |
| 190 | Collection<BridgeDescription> bridges = bridgeConfig.getBridges(); |
| 191 | for (BridgeDescription br: bridges) { |
| 192 | if (Objects.equals(bridgeName, br.name())) { |
| 193 | return Optional.of(br); |
| 194 | } |
| 195 | } |
| 196 | } catch (Exception e) { |
| 197 | log.error("Exception : ", e); |
| 198 | } |
| 199 | return Optional.empty(); |
| 200 | } |
| 201 | |
| 202 | /** |
| 203 | * Builds OVSDB device id. |
| 204 | * @param mgmtIp management ip address string |
| 205 | * @return OVSDB device id |
| 206 | */ |
| 207 | public static final DeviceId ovsdbDeviceId(String mgmtIp) { |
| 208 | return DeviceId.deviceId(OVSDB_DEVICE_PREFIX.concat(mgmtIp)); |
| 209 | } |
| 210 | |
| 211 | /** |
| 212 | * Returns {@code true} if this bridge is available; |
| 213 | * returns {@code false} otherwise. |
| 214 | * @param context workflow context |
| 215 | * @param devId device id |
| 216 | * @return {@code true} if this bridge is available; {@code false} otherwise. |
| 217 | * @throws WorkflowException workflow exception |
| 218 | */ |
| 219 | public static final boolean isAvailableBridge(WorkflowContext context, DeviceId devId) |
| 220 | throws WorkflowException { |
| 221 | |
| 222 | if (Objects.isNull(devId)) { |
| 223 | throw new WorkflowException("Invalid device id in data model"); |
| 224 | } |
| 225 | |
| 226 | DeviceService deviceService = context.getService(DeviceService.class); |
| 227 | Device dev = deviceService.getDevice(devId); |
| 228 | if (Objects.isNull(dev)) { |
| 229 | return false; |
| 230 | } |
| 231 | |
| 232 | return deviceService.isAvailable(devId); |
| 233 | } |
| 234 | |
| 235 | /** |
| 236 | * Gets openflow controller information list. |
| 237 | * @param context workflow context |
| 238 | * @return openflow controller information list |
| 239 | * @throws WorkflowException workflow exception |
| 240 | */ |
| 241 | public static final List<ControllerInfo> getOpenflowControllerInfoList(WorkflowContext context) |
| 242 | throws WorkflowException { |
| 243 | ClusterService clusterService = context.getService(ClusterService.class); |
| 244 | java.util.List<org.onosproject.net.behaviour.ControllerInfo> controllers = new ArrayList<>(); |
| 245 | Sets.newHashSet(clusterService.getNodes()).forEach( |
| 246 | controller -> { |
| 247 | org.onosproject.net.behaviour.ControllerInfo ctrlInfo = |
| 248 | new org.onosproject.net.behaviour.ControllerInfo(controller.ip(), |
| 249 | OPENFLOW_PORT, |
| 250 | OPENFLOW_CHANNEL_PROTO); |
| 251 | controllers.add(ctrlInfo); |
| 252 | } |
| 253 | ); |
| 254 | return controllers; |
| 255 | } |
| 256 | |
| 257 | /** |
| 258 | * Creates bridge. |
| 259 | * @param bridgeConfig bridge config |
| 260 | * @param name bridge name to create |
| 261 | * @param dpid openflow data path id of bridge to create |
| 262 | * @param ofControllers openflow controller information list |
| 263 | * @param datapathType OVS data path type |
| 264 | */ |
| 265 | public static final void createBridge(BridgeConfig bridgeConfig, String name, String dpid, |
| 266 | List<ControllerInfo> ofControllers, OvsDatapathType datapathType) { |
| 267 | BridgeDescription.Builder bridgeDescBuilder = DefaultBridgeDescription.builder() |
| 268 | .name(name) |
| 269 | .failMode(BridgeDescription.FailMode.SECURE) |
| 270 | .datapathId(dpid) |
| 271 | .disableInBand() |
| 272 | .controlProtocols(Collections.singletonList(BRIDGE_DEFAULT_OF_VERSION)) |
| 273 | .controllers(ofControllers); |
| 274 | |
| 275 | if (datapathType != null && !(datapathType.equals(OvsDatapathType.EMPTY))) { |
| 276 | bridgeDescBuilder.datapathType(datapathType.toString()); |
| 277 | log.info("create {} with dataPathType {}", name, datapathType); |
| 278 | } |
| 279 | |
| 280 | BridgeDescription bridgeDesc = bridgeDescBuilder.build(); |
| 281 | bridgeConfig.addBridge(bridgeDesc); |
| 282 | } |
| 283 | |
| 284 | /** |
| 285 | * Index of data path id in openflow device id. |
| 286 | */ |
| 287 | private static final int DPID_BEGIN_INDEX = 3; |
| 288 | |
| 289 | /** |
| 290 | * Gets bridge data path id. |
| 291 | * @param devId device id |
| 292 | * @return bridge data path id |
| 293 | */ |
| 294 | public static final String bridgeDatapathId(DeviceId devId) { |
| 295 | return devId.toString().substring(DPID_BEGIN_INDEX); |
| 296 | } |
| 297 | |
| 298 | /** |
| 299 | * Gets OVSDB client. |
| 300 | * @param context workflow context |
| 301 | * @param strMgmtIp management ip address |
| 302 | * @param intOvsdbPort OVSDB port |
| 303 | * @return ovsdb client |
| 304 | * @throws WorkflowException workflow exception |
| 305 | */ |
| 306 | public static final OvsdbClientService getOvsdbClient( |
| 307 | WorkflowContext context, String strMgmtIp, int intOvsdbPort) throws WorkflowException { |
| 308 | IpAddress mgmtIp = IpAddress.valueOf(strMgmtIp); |
| 309 | TpPort ovsdbPort = TpPort.tpPort(intOvsdbPort); |
| 310 | OvsdbController ovsdbController = context.getService(OvsdbController.class); |
| 311 | return ovsdbController.getOvsdbClient(new OvsdbNodeId(mgmtIp, ovsdbPort.toInt())); |
| 312 | } |
| 313 | |
| 314 | /** |
| 315 | * Checks whether 2 controller informations include same controller information. |
| 316 | * @param a controller information list |
| 317 | * @param b controller information list |
| 318 | * @return {@code true} if 2 controller informations include same controller information |
| 319 | */ |
| 320 | public static boolean isEqual(List<ControllerInfo> a, List<ControllerInfo> b) { |
| 321 | if (a == b) { |
| 322 | return true; |
| 323 | } else if (a == null) { |
| 324 | // equivalent to (a == null && b != null) |
| 325 | return false; |
| 326 | } else if (b == null) { |
| 327 | // equivalent to (a != null && b == null) |
| 328 | return false; |
| 329 | } else if (a.size() != b.size()) { |
| 330 | return false; |
| 331 | } |
| 332 | |
| 333 | return a.containsAll(b); |
| 334 | } |
| 335 | |
| 336 | /** |
| 337 | * Gets the name of the port. |
| 338 | * @param port port |
| 339 | * @return the name of the port |
| 340 | */ |
| 341 | public static final String portName(Port port) { |
| 342 | return port.annotations().value(PORT_NAME); |
| 343 | } |
| 344 | } |
| 345 | |
| 346 | /** |
| 347 | * Work-let class for creating OVSDB device. |
| 348 | */ |
| 349 | public static class CreateOvsdbDevice extends AbstractWorklet { |
| 350 | |
| 351 | @JsonDataModel(path = MODEL_MGMT_IP) |
| 352 | String strMgmtIp; |
| 353 | |
| 354 | @JsonDataModel(path = MODEL_OVSDB_PORT) |
| 355 | Integer intOvsdbPort; |
| 356 | |
| 357 | @Override |
| 358 | public boolean isNext(WorkflowContext context) throws WorkflowException { |
| 359 | |
| 360 | OvsdbClientService ovsdbClient = OvsUtil.getOvsdbClient(context, strMgmtIp, intOvsdbPort); |
| 361 | return ovsdbClient == null || !ovsdbClient.isConnected(); |
| 362 | } |
| 363 | |
| 364 | @Override |
| 365 | public void process(WorkflowContext context) throws WorkflowException { |
| 366 | IpAddress mgmtIp = IpAddress.valueOf(strMgmtIp); |
| 367 | TpPort ovsdbPort = TpPort.tpPort(intOvsdbPort); |
| 368 | OvsdbController ovsdbController = context.getService(OvsdbController.class); |
| 369 | context.waitCompletion(DeviceEvent.class, OVSDB_DEVICE_PREFIX.concat(strMgmtIp), |
| 370 | () -> ovsdbController.connect(mgmtIp, ovsdbPort), |
| 371 | TIMEOUT_DEVICE_CREATION_MS |
| 372 | ); |
| 373 | } |
| 374 | |
| 375 | @Override |
| 376 | public boolean isCompleted(WorkflowContext context, Event event)throws WorkflowException { |
| 377 | if (!(event instanceof DeviceEvent)) { |
| 378 | return false; |
| 379 | } |
| 380 | DeviceEvent deviceEvent = (DeviceEvent) event; |
| 381 | Device device = deviceEvent.subject(); |
| 382 | switch (deviceEvent.type()) { |
| 383 | case DEVICE_ADDED: |
| 384 | case DEVICE_AVAILABILITY_CHANGED: |
| 385 | case DEVICE_UPDATED: |
| 386 | return context.getService(DeviceService.class).isAvailable(device.id()); |
| 387 | default: |
| 388 | return false; |
| 389 | } |
| 390 | } |
| 391 | |
| 392 | @Override |
| 393 | public void timeout(WorkflowContext context) throws WorkflowException { |
| 394 | if (!isNext(context)) { |
| 395 | context.completed(); //Complete the job of worklet by timeout |
| 396 | } else { |
| 397 | super.timeout(context); |
| 398 | } |
| 399 | } |
| 400 | } |
| 401 | |
| 402 | |
| 403 | /** |
| 404 | * Work-let class for removing OVSDB device. |
| 405 | */ |
| 406 | public static class RemoveOvsdbDevice extends AbstractWorklet { |
| 407 | |
| 408 | @JsonDataModel(path = MODEL_MGMT_IP) |
| 409 | String strMgmtIp; |
| 410 | |
| 411 | @Override |
| 412 | public boolean isNext(WorkflowContext context) throws WorkflowException { |
| 413 | |
| 414 | DeviceId devId = DeviceId.deviceId(OVSDB_DEVICE_PREFIX.concat(strMgmtIp)); |
| 415 | |
| 416 | Device dev = context.getService(DeviceService.class).getDevice(devId); |
| 417 | return dev != null; |
| 418 | } |
| 419 | |
| 420 | @Override |
| 421 | public void process(WorkflowContext context) throws WorkflowException { |
| 422 | IpAddress mgmtIp = IpAddress.valueOf(strMgmtIp); |
| 423 | check(mgmtIp != null, "mgmt ip is invalid"); |
| 424 | DeviceId devId = DeviceId.deviceId(OVSDB_DEVICE_PREFIX.concat(strMgmtIp)); |
| 425 | DeviceAdminService adminService = context.getService(DeviceAdminService.class); |
| 426 | |
| 427 | context.waitCompletion(DeviceEvent.class, devId.toString(), |
| 428 | () -> adminService.removeDevice(devId), |
| 429 | TIMEOUT_DEVICE_CREATION_MS |
| 430 | ); |
| 431 | } |
| 432 | |
| 433 | @Override |
| 434 | public boolean isCompleted(WorkflowContext context, Event event)throws WorkflowException { |
| 435 | if (!(event instanceof DeviceEvent)) { |
| 436 | return false; |
| 437 | } |
| 438 | DeviceEvent deviceEvent = (DeviceEvent) event; |
| 439 | switch (deviceEvent.type()) { |
| 440 | case DEVICE_REMOVED: |
| 441 | return !isNext(context); |
| 442 | default: |
| 443 | return false; |
| 444 | } |
| 445 | } |
| 446 | |
| 447 | @Override |
| 448 | public void timeout(WorkflowContext context) throws WorkflowException { |
| 449 | if (!isNext(context)) { |
| 450 | context.completed(); //Complete worklet by timeout |
| 451 | } else { |
| 452 | super.timeout(context); |
| 453 | } |
| 454 | } |
| 455 | } |
| 456 | |
| 457 | /** |
| 458 | * Work-let class for updating OVS version. |
| 459 | */ |
| 460 | public static class UpdateOvsVersion extends AbstractWorklet { |
| 461 | |
| 462 | @JsonDataModel(path = MODEL_OVS_VERSION, optional = true) |
| 463 | String strOvsVersion; |
| 464 | |
| 465 | @JsonDataModel(path = MODEL_SSH_ACCESSINFO) |
| 466 | JsonNode strSshAccessInfo; |
| 467 | |
| 468 | @Override |
| 469 | public boolean isNext(WorkflowContext context) throws WorkflowException { |
| 470 | |
| 471 | return strOvsVersion == null; |
| 472 | } |
| 473 | |
| 474 | @Override |
| 475 | public void process(WorkflowContext context) throws WorkflowException { |
| 476 | |
| 477 | SshAccessInfo sshAccessInfo = SshAccessInfo.valueOf(strSshAccessInfo); |
| 478 | check(Objects.nonNull(sshAccessInfo), "Invalid ssh access info " + context.data()); |
| 479 | |
| 480 | OvsVersion ovsVersion = SshUtil.exec(sshAccessInfo, |
| 481 | session -> SshUtil.fetchOvsVersion(session)); |
| 482 | |
| 483 | check(Objects.nonNull(ovsVersion), "Failed to fetch ovs version " + context.data()); |
| 484 | strOvsVersion = ovsVersion.toString(); |
| 485 | |
| 486 | context.completed(); |
| 487 | } |
| 488 | } |
| 489 | |
| 490 | /** |
| 491 | * Work-let class for updating overlay bridge device id. |
| 492 | */ |
| 493 | public static class UpdateOverlayBridgeId extends AbstractWorklet { |
| 494 | |
| 495 | @JsonDataModel(path = MODEL_MGMT_IP) |
| 496 | String strMgmtIp; |
| 497 | |
| 498 | @JsonDataModel(path = MODEL_OF_DEVID_OVERLAY_BRIDGE, optional = true) |
| 499 | String strOfDevIdOverlay; |
| 500 | |
| 501 | @Override |
| 502 | public boolean isNext(WorkflowContext context) throws WorkflowException { |
| 503 | |
| 504 | return strOfDevIdOverlay == null; |
| 505 | } |
| 506 | |
| 507 | @Override |
| 508 | public void process(WorkflowContext context) throws WorkflowException { |
| 509 | |
| 510 | BridgeConfig bridgeConfig = OvsUtil.getOvsdbBehaviour(context, strMgmtIp, BridgeConfig.class); |
| 511 | Optional<BridgeDescription> optBd = OvsUtil.getBridgeDescription(bridgeConfig, BRIDGE_OVERLAY); |
| 512 | if (optBd.isPresent()) { |
| 513 | Optional<DeviceId> optDevId = optBd.get().deviceId(); |
| 514 | if (optDevId.isPresent()) { |
| 515 | log.info("Updates {} of device id with existing device id {}", BRIDGE_OVERLAY, optDevId.get()); |
| 516 | strOfDevIdOverlay = optDevId.get().toString(); |
| 517 | } else { |
| 518 | DeviceId newDevId = OvsUtil.buildOfDeviceId(IpAddress.valueOf(strMgmtIp), DEVID_IDX_BRIDGE_OVERLAY); |
| 519 | log.info("Failed to find devId. Updates {} of device id with new device id {}", |
| 520 | BRIDGE_OVERLAY, newDevId); |
| 521 | strOfDevIdOverlay = newDevId.toString(); |
| 522 | } |
| 523 | } else { |
| 524 | DeviceId newDevId = OvsUtil.buildOfDeviceId(IpAddress.valueOf(strMgmtIp), DEVID_IDX_BRIDGE_OVERLAY); |
| 525 | log.info("Failed to find description. Updates {} of device id with new device id {}", |
| 526 | BRIDGE_OVERLAY, newDevId); |
| 527 | strOfDevIdOverlay = newDevId.toString(); |
| 528 | } |
| 529 | |
| 530 | context.completed(); |
| 531 | } |
| 532 | } |
| 533 | |
| 534 | /** |
| 535 | * Work-let class for creating overlay openflow bridge. |
| 536 | */ |
| 537 | public static class CreateOverlayBridge extends AbstractWorklet { |
| 538 | |
| 539 | @JsonDataModel(path = MODEL_MGMT_IP) |
| 540 | String strMgmtIp; |
| 541 | |
| 542 | @JsonDataModel(path = MODEL_OVSDB_PORT) |
| 543 | Integer intOvsdbPort; |
| 544 | |
| 545 | @JsonDataModel(path = MODEL_OVS_DATAPATH_TYPE) |
| 546 | String strOvsDatapath; |
| 547 | |
| 548 | @JsonDataModel(path = MODEL_OF_DEVID_OVERLAY_BRIDGE, optional = true) |
| 549 | String strOfDevIdOverlay; |
| 550 | |
| 551 | @Override |
| 552 | public boolean isNext(WorkflowContext context) throws WorkflowException { |
| 553 | |
| 554 | check(strOfDevIdOverlay != null, "invalid strOfDevIdOverlay"); |
| 555 | return !OvsUtil.isAvailableBridge(context, DeviceId.deviceId(strOfDevIdOverlay)); |
| 556 | } |
| 557 | |
| 558 | @Override |
| 559 | public void process(WorkflowContext context) throws WorkflowException { |
| 560 | |
| 561 | check(strOfDevIdOverlay != null, "invalid strOfDevIdOverlay"); |
| 562 | BridgeConfig bridgeConfig = OvsUtil.getOvsdbBehaviour(context, strMgmtIp, BridgeConfig.class); |
| 563 | List<ControllerInfo> ofControllers = OvsUtil.getOpenflowControllerInfoList(context); |
| 564 | DeviceId ofDeviceId = DeviceId.deviceId(strOfDevIdOverlay); |
| 565 | |
| 566 | if (ofControllers == null || ofControllers.size() == 0) { |
| 567 | throw new WorkflowException("Invalid of controllers"); |
| 568 | } |
| 569 | |
| 570 | Optional<BridgeDescription> optBd = OvsUtil.getBridgeDescription(bridgeConfig, BRIDGE_OVERLAY); |
| 571 | if (!optBd.isPresent()) { |
| 572 | |
| 573 | // If bridge does not exist, just creates a new bridge. |
| 574 | context.waitCompletion(DeviceEvent.class, ofDeviceId.toString(), |
| 575 | () -> OvsUtil.createBridge(bridgeConfig, |
| 576 | BRIDGE_OVERLAY, |
| 577 | OvsUtil.bridgeDatapathId(ofDeviceId), |
| 578 | ofControllers, |
| 579 | OvsUtil.buildOvsDatapathType(strOvsDatapath)), |
| 580 | TIMEOUT_DEVICE_CREATION_MS |
| 581 | ); |
| 582 | return; |
| 583 | |
| 584 | } else { |
| 585 | BridgeDescription bd = optBd.get(); |
| 586 | if (OvsUtil.isEqual(ofControllers, bd.controllers())) { |
| 587 | log.error("{} has valid controller setting({})", BRIDGE_OVERLAY, bd.controllers()); |
| 588 | context.completed(); |
| 589 | return; |
| 590 | } |
| 591 | |
| 592 | OvsdbClientService ovsdbClient = OvsUtil.getOvsdbClient(context, strMgmtIp, intOvsdbPort); |
| 593 | if (ovsdbClient == null || !ovsdbClient.isConnected()) { |
| 594 | throw new WorkflowException("Invalid ovsdb client for " + strMgmtIp); |
| 595 | } |
| 596 | |
| 597 | // If controller settings are not matched, set controller with valid controller information. |
| 598 | context.waitCompletion(DeviceEvent.class, ofDeviceId.toString(), |
| 599 | () -> ovsdbClient.setControllersWithDeviceId(bd.deviceId().get(), ofControllers), |
| 600 | TIMEOUT_DEVICE_CREATION_MS |
| 601 | ); |
| 602 | return; |
| 603 | } |
| 604 | } |
| 605 | |
| 606 | @Override |
| 607 | public boolean isCompleted(WorkflowContext context, Event event)throws WorkflowException { |
| 608 | if (!(event instanceof DeviceEvent)) { |
| 609 | return false; |
| 610 | } |
| 611 | DeviceEvent deviceEvent = (DeviceEvent) event; |
| 612 | Device device = deviceEvent.subject(); |
| 613 | switch (deviceEvent.type()) { |
| 614 | case DEVICE_ADDED: |
| 615 | case DEVICE_AVAILABILITY_CHANGED: |
| 616 | case DEVICE_UPDATED: |
| 617 | return context.getService(DeviceService.class).isAvailable(device.id()); |
| 618 | default: |
| 619 | return false; |
| 620 | } |
| 621 | } |
| 622 | |
| 623 | @Override |
| 624 | public void timeout(WorkflowContext context) throws WorkflowException { |
| 625 | if (!isNext(context)) { |
| 626 | context.completed(); //Complete the job of worklet by timeout |
| 627 | } else { |
| 628 | super.timeout(context); |
| 629 | } |
| 630 | } |
| 631 | } |
| 632 | |
| 633 | /** |
| 634 | * Work-let class for updating underlay bridge device id. |
| 635 | */ |
| 636 | public static class UpdateUnderlayBridgeId extends AbstractWorklet { |
| 637 | |
| 638 | @JsonDataModel(path = MODEL_MGMT_IP) |
| 639 | String strMgmtIp; |
| 640 | |
| 641 | @JsonDataModel(path = MODEL_OF_DEVID_UNDERLAY_BRIDGE, optional = true) |
| 642 | String strOfDevIdUnderlay; |
| 643 | |
| 644 | @Override |
| 645 | public boolean isNext(WorkflowContext context) throws WorkflowException { |
| 646 | |
| 647 | return strOfDevIdUnderlay == null; |
| 648 | } |
| 649 | |
| 650 | @Override |
| 651 | public void process(WorkflowContext context) throws WorkflowException { |
| 652 | |
| 653 | BridgeConfig bridgeConfig = OvsUtil.getOvsdbBehaviour(context, strMgmtIp, BridgeConfig.class); |
| 654 | Optional<BridgeDescription> optBd = OvsUtil.getBridgeDescription(bridgeConfig, BRIDGE_UNDERLAY); |
| 655 | if (optBd.isPresent()) { |
| 656 | Optional<DeviceId> optDevId = optBd.get().deviceId(); |
| 657 | if (optDevId.isPresent()) { |
| 658 | log.info("Updates {} of device id with existing device id {}", BRIDGE_UNDERLAY, optDevId.get()); |
| 659 | strOfDevIdUnderlay = optDevId.get().toString(); |
| 660 | } else { |
| 661 | DeviceId devId = OvsUtil.buildOfDeviceId(IpAddress.valueOf(strMgmtIp), |
| 662 | DEVID_IDX_BRIDGE_UNDERLAY_NOVA); |
| 663 | log.info("Failed to find devId. Updates {} of device id with new device id {}", |
| 664 | BRIDGE_UNDERLAY, devId); |
| 665 | strOfDevIdUnderlay = devId.toString(); |
| 666 | } |
| 667 | } else { |
| 668 | DeviceId devId = OvsUtil.buildOfDeviceId(IpAddress.valueOf(strMgmtIp), DEVID_IDX_BRIDGE_UNDERLAY_NOVA); |
| 669 | log.info("Failed to find description. Updates {} of device id with new device id {}", |
| 670 | BRIDGE_UNDERLAY, devId); |
| 671 | strOfDevIdUnderlay = devId.toString(); |
| 672 | } |
| 673 | |
| 674 | context.completed(); |
| 675 | } |
| 676 | } |
| 677 | |
| 678 | /** |
| 679 | * Work-let class for creating underlay openflow bridge. |
| 680 | */ |
| 681 | public static class CreateUnderlayBridge extends AbstractWorklet { |
| 682 | |
| 683 | @JsonDataModel(path = MODEL_MGMT_IP) |
| 684 | String strMgmtIp; |
| 685 | |
| 686 | @JsonDataModel(path = MODEL_OVSDB_PORT) |
| 687 | Integer intOvsdbPort; |
| 688 | |
| 689 | @JsonDataModel(path = MODEL_OVS_DATAPATH_TYPE) |
| 690 | String strOvsDatapath; |
| 691 | |
| 692 | @JsonDataModel(path = MODEL_OF_DEVID_UNDERLAY_BRIDGE) |
| 693 | String strOfDevIdUnderlay; |
| 694 | |
| 695 | @Override |
| 696 | public boolean isNext(WorkflowContext context) throws WorkflowException { |
| 697 | |
| 698 | check(strOfDevIdUnderlay != null, "invalid strOfDevIdUnderlay"); |
| 699 | return !OvsUtil.isAvailableBridge(context, DeviceId.deviceId(strOfDevIdUnderlay)); |
| 700 | } |
| 701 | |
| 702 | @Override |
| 703 | public void process(WorkflowContext context) throws WorkflowException { |
| 704 | |
| 705 | check(strOfDevIdUnderlay != null, "invalid strOfDevIdUnderlay"); |
| 706 | BridgeConfig bridgeConfig = OvsUtil.getOvsdbBehaviour(context, strMgmtIp, BridgeConfig.class); |
| 707 | List<ControllerInfo> ofControllers = OvsUtil.getOpenflowControllerInfoList(context); |
| 708 | DeviceId ofDeviceId = DeviceId.deviceId(strOfDevIdUnderlay); |
| 709 | |
| 710 | if (ofControllers == null || ofControllers.size() == 0) { |
| 711 | throw new WorkflowException("Invalid of controllers"); |
| 712 | } |
| 713 | |
| 714 | Optional<BridgeDescription> optBd = OvsUtil.getBridgeDescription(bridgeConfig, BRIDGE_UNDERLAY); |
| 715 | if (!optBd.isPresent()) { |
| 716 | |
| 717 | // If bridge does not exist, just creates a new bridge. |
| 718 | context.waitCompletion(DeviceEvent.class, ofDeviceId.toString(), |
| 719 | () -> OvsUtil.createBridge(bridgeConfig, |
| 720 | BRIDGE_UNDERLAY, |
| 721 | OvsUtil.bridgeDatapathId(ofDeviceId), |
| 722 | ofControllers, |
| 723 | OvsUtil.buildOvsDatapathType(strOvsDatapath)), |
| 724 | TIMEOUT_DEVICE_CREATION_MS |
| 725 | ); |
| 726 | return; |
| 727 | |
| 728 | } else { |
| 729 | BridgeDescription bd = optBd.get(); |
| 730 | if (OvsUtil.isEqual(ofControllers, bd.controllers())) { |
| 731 | log.error("{} has valid controller setting({})", BRIDGE_UNDERLAY, bd.controllers()); |
| 732 | context.completed(); |
| 733 | return; |
| 734 | } |
| 735 | |
| 736 | OvsdbClientService ovsdbClient = OvsUtil.getOvsdbClient(context, strMgmtIp, intOvsdbPort); |
| 737 | if (ovsdbClient == null || !ovsdbClient.isConnected()) { |
| 738 | throw new WorkflowException("Invalid ovsdb client for " + strMgmtIp); |
| 739 | } |
| 740 | |
| 741 | // If controller settings are not matched, set controller with valid controller information. |
| 742 | context.waitCompletion(DeviceEvent.class, ofDeviceId.toString(), |
| 743 | () -> ovsdbClient.setControllersWithDeviceId(bd.deviceId().get(), ofControllers), |
| 744 | TIMEOUT_DEVICE_CREATION_MS |
| 745 | ); |
| 746 | return; |
| 747 | } |
| 748 | } |
| 749 | |
| 750 | @Override |
| 751 | public boolean isCompleted(WorkflowContext context, Event event)throws WorkflowException { |
| 752 | if (!(event instanceof DeviceEvent)) { |
| 753 | return false; |
| 754 | } |
| 755 | DeviceEvent deviceEvent = (DeviceEvent) event; |
| 756 | Device device = deviceEvent.subject(); |
| 757 | switch (deviceEvent.type()) { |
| 758 | case DEVICE_ADDED: |
| 759 | case DEVICE_AVAILABILITY_CHANGED: |
| 760 | case DEVICE_UPDATED: |
| 761 | return context.getService(DeviceService.class).isAvailable(device.id()); |
| 762 | default: |
| 763 | return false; |
| 764 | } |
| 765 | } |
| 766 | |
| 767 | @Override |
| 768 | public void timeout(WorkflowContext context) throws WorkflowException { |
| 769 | if (!isNext(context)) { |
| 770 | context.completed(); //Complete the job of worklet by timeout |
| 771 | } else { |
| 772 | super.timeout(context); |
| 773 | } |
| 774 | } |
| 775 | |
| 776 | } |
| 777 | |
| 778 | /** |
| 779 | * Work-let class for creating vxlan port on the overlay bridge. |
| 780 | */ |
| 781 | public static class CreateOverlayBridgeVxlanPort extends AbstractWorklet { |
| 782 | |
| 783 | @JsonDataModel(path = MODEL_MGMT_IP) |
| 784 | String strMgmtIp; |
| 785 | |
| 786 | @JsonDataModel(path = MODEL_OF_DEVID_OVERLAY_BRIDGE, optional = true) |
| 787 | String strOfDevIdOverlay; |
| 788 | |
| 789 | private static final String OVS_VXLAN_PORTNAME = "vxlan"; |
| 790 | |
| 791 | @Override |
| 792 | public boolean isNext(WorkflowContext context) throws WorkflowException { |
| 793 | |
| 794 | check(strOfDevIdOverlay != null, "invalid strOfDevIdOverlay"); |
| 795 | DeviceId deviceId = DeviceId.deviceId(strOfDevIdOverlay); |
| 796 | if (Objects.isNull(deviceId)) { |
| 797 | throw new WorkflowException("Invalid br-int bridge, before creating VXLAN port"); |
| 798 | } |
| 799 | |
| 800 | DeviceService deviceService = context.getService(DeviceService.class); |
| 801 | return !deviceService.getPorts(deviceId) |
| 802 | .stream() |
| 803 | .filter(port -> OvsUtil.portName(port).contains(OVS_VXLAN_PORTNAME) && port.isEnabled()) |
| 804 | .findAny().isPresent(); |
| 805 | } |
| 806 | |
| 807 | |
| 808 | @Override |
| 809 | public void process(WorkflowContext context) throws WorkflowException { |
| 810 | |
| 811 | check(strOfDevIdOverlay != null, "invalid strOfDevIdOverlay"); |
| 812 | TunnelDescription description = DefaultTunnelDescription.builder() |
| 813 | .deviceId(BRIDGE_OVERLAY) |
| 814 | .ifaceName(OVS_VXLAN_PORTNAME) |
| 815 | .type(TunnelDescription.Type.VXLAN) |
| 816 | .remote(TunnelEndPoints.flowTunnelEndpoint()) |
| 817 | .key(TunnelKeys.flowTunnelKey()) |
| 818 | .build(); |
| 819 | |
| 820 | DeviceId ofDeviceId = DeviceId.deviceId(strOfDevIdOverlay); |
| 821 | InterfaceConfig interfaceConfig = OvsUtil.getOvsdbBehaviour(context, strMgmtIp, InterfaceConfig.class); |
| 822 | |
| 823 | context.waitCompletion(DeviceEvent.class, ofDeviceId.toString(), |
| 824 | () -> interfaceConfig.addTunnelMode(BRIDGE_OVERLAY, description), |
| 825 | TIMEOUT_DEVICE_CREATION_MS |
| 826 | ); |
| 827 | } |
| 828 | |
| 829 | @Override |
| 830 | public boolean isCompleted(WorkflowContext context, Event event)throws WorkflowException { |
| 831 | if (!(event instanceof DeviceEvent)) { |
| 832 | return false; |
| 833 | } |
| 834 | DeviceEvent deviceEvent = (DeviceEvent) event; |
| 835 | switch (deviceEvent.type()) { |
| 836 | case PORT_ADDED: |
| 837 | return !isNext(context); |
| 838 | default: |
| 839 | return false; |
| 840 | } |
| 841 | } |
| 842 | |
| 843 | @Override |
| 844 | public void timeout(WorkflowContext context) throws WorkflowException { |
| 845 | if (!isNext(context)) { |
| 846 | context.completed(); //Complete the job of worklet by timeout |
| 847 | } else { |
| 848 | super.timeout(context); |
| 849 | } |
| 850 | } |
| 851 | } |
| 852 | |
| 853 | /** |
| 854 | * Work-let class for adding physical ports on the underlay openflow bridge. |
| 855 | */ |
| 856 | public static class AddPhysicalPortsOnUnderlayBridge extends AbstractWorklet { |
| 857 | |
| 858 | @JsonDataModel(path = MODEL_MGMT_IP) |
| 859 | String strMgmtIp; |
| 860 | |
| 861 | @JsonDataModel(path = MODEL_OVSDB_PORT) |
| 862 | Integer intOvsdbPort; |
| 863 | |
| 864 | @JsonDataModel(path = MODEL_OF_DEVID_UNDERLAY_BRIDGE, optional = true) |
| 865 | String strOfDevIdUnderlay; |
| 866 | |
| 867 | @JsonDataModel(path = MODEL_OVS_DATAPATH_TYPE) |
| 868 | String strOvsDatapath; |
| 869 | |
| 870 | @JsonDataModel(path = MODEL_PHY_PORTS) |
| 871 | ArrayNode arrNodePhysicalPorts; |
| 872 | |
| 873 | @Override |
| 874 | public boolean isNext(WorkflowContext context) throws WorkflowException { |
| 875 | check(strOfDevIdUnderlay != null, "invalid strOfDevIdUnderlay"); |
| 876 | DeviceId brphyDevId = DeviceId.deviceId(strOfDevIdUnderlay); |
| 877 | return !hasAllPhysicalPorts(context, brphyDevId); |
| 878 | } |
| 879 | |
| 880 | @Override |
| 881 | public void process(WorkflowContext context) throws WorkflowException { |
| 882 | check(strOfDevIdUnderlay != null, "invalid strOfDevIdUnderlay"); |
| 883 | DeviceId brphyDevId = DeviceId.deviceId(strOfDevIdUnderlay); |
| 884 | |
| 885 | context.waitCompletion(DeviceEvent.class, brphyDevId.toString(), |
| 886 | () -> addPhysicalPorts(context, brphyDevId, BRIDGE_UNDERLAY, strOvsDatapath), |
| 887 | TIMEOUT_PORT_ADDITION_MS |
| 888 | ); |
| 889 | } |
| 890 | |
| 891 | @Override |
| 892 | public boolean isCompleted(WorkflowContext context, Event event)throws WorkflowException { |
| 893 | if (!(event instanceof DeviceEvent)) { |
| 894 | return false; |
| 895 | } |
| 896 | DeviceEvent deviceEvent = (DeviceEvent) event; |
| 897 | switch (deviceEvent.type()) { |
| 898 | case PORT_ADDED: |
| 899 | return !isNext(context); |
| 900 | default: |
| 901 | return false; |
| 902 | } |
| 903 | } |
| 904 | |
| 905 | @Override |
| 906 | public void timeout(WorkflowContext context) throws WorkflowException { |
| 907 | if (!isNext(context)) { |
| 908 | context.completed(); //Complete the job of worklet by timeout |
| 909 | } else { |
| 910 | super.timeout(context); |
| 911 | } |
| 912 | } |
| 913 | |
| 914 | private final List<String> getPhysicalPorts(WorkflowContext context) throws WorkflowException { |
| 915 | List<String> ports = Lists.newArrayList(); |
| 916 | for (JsonNode jsonNode : arrNodePhysicalPorts) { |
| 917 | check(jsonNode instanceof TextNode, "Invalid physical ports " + arrNodePhysicalPorts); |
| 918 | ports.add(jsonNode.asText()); |
| 919 | } |
| 920 | return ports; |
| 921 | } |
| 922 | |
| 923 | private final boolean hasAllPhysicalPorts(WorkflowContext context, DeviceId devId) throws WorkflowException { |
| 924 | |
| 925 | List<Port> devPorts = context.getService(DeviceService.class).getPorts(devId); |
| 926 | check(devPorts != null, "Invalid device ports for " + devId); |
| 927 | List<String> physicalPorts = getPhysicalPorts(context); |
| 928 | check(physicalPorts != null, "Invalid physical ports" + context); |
| 929 | |
| 930 | log.info("physicalPorts: {} for {}", physicalPorts, devId); |
| 931 | for (String port: physicalPorts) { |
| 932 | if (devPorts.stream().noneMatch(p -> OvsUtil.portName(p).contains(port))) { |
| 933 | return false; |
| 934 | } |
| 935 | } |
| 936 | return true; |
| 937 | } |
| 938 | |
| 939 | private final boolean hasPort(WorkflowContext context, DeviceId devId, String portName) |
| 940 | throws WorkflowException { |
| 941 | |
| 942 | List<Port> devPorts = context.getService(DeviceService.class).getPorts(devId); |
| 943 | check(devPorts != null, "Invalid device ports for " + devId); |
| 944 | return devPorts.stream().anyMatch(p -> OvsUtil.portName(p).contains(portName)); |
| 945 | } |
| 946 | |
| 947 | private final void addPhysicalPorts(WorkflowContext context, DeviceId devId, String bridgeName, |
| 948 | String strOvsDatapathType) |
| 949 | throws WorkflowException { |
| 950 | OvsdbClientService ovsdbClient = OvsUtil.getOvsdbClient(context, strMgmtIp, intOvsdbPort); |
| 951 | check(ovsdbClient != null, "Invalid ovsdb client"); |
| 952 | |
| 953 | List<String> physicalPorts = getPhysicalPorts(context); |
| 954 | check(physicalPorts != null, "Invalid physical ports"); |
| 955 | |
| 956 | OvsDatapathType datapathType = OvsUtil.buildOvsDatapathType(strOvsDatapathType); |
| 957 | check(datapathType != null, "Invalid data path type"); |
| 958 | |
| 959 | List<String> sortedPhyPorts = physicalPorts.stream().sorted().collect(Collectors.toList()); |
| 960 | |
| 961 | for (String port: sortedPhyPorts) { |
| 962 | if (hasPort(context, devId, port)) { |
| 963 | continue; |
| 964 | } |
| 965 | log.info("adding port {} on {}", port, devId); |
| 966 | switch (datapathType) { |
| 967 | case NETDEV: |
| 968 | throw new WorkflowException("NETDEV datapathType are not supported"); |
| 969 | //break; |
| 970 | case SYSTEM: |
| 971 | default: |
| 972 | ovsdbClient.createPort(BridgeName.bridgeName(bridgeName).name(), port); |
| 973 | } |
| 974 | } |
| 975 | } |
| 976 | } |
| 977 | |
| 978 | /** |
| 979 | * Work-let class for configure local ip of underlay openflow bridge. |
| 980 | */ |
| 981 | public static class ConfigureUnderlayBridgeLocalIp extends AbstractWorklet { |
| 982 | |
| 983 | @JsonDataModel(path = MODEL_SSH_ACCESSINFO) |
| 984 | JsonNode strSshAccessInfo; |
| 985 | |
| 986 | @JsonDataModel(path = MODEL_VTEP_IP) |
| 987 | String strVtepIp; |
| 988 | |
| 989 | @Override |
| 990 | public boolean isNext(WorkflowContext context) throws WorkflowException { |
| 991 | |
| 992 | SshAccessInfo sshAccessInfo = SshAccessInfo.valueOf(strSshAccessInfo); |
| 993 | check(Objects.nonNull(sshAccessInfo), "Invalid ssh access info " + context.data()); |
| 994 | |
| 995 | NetworkAddress vtepIp = NetworkAddress.valueOf(strVtepIp); |
| 996 | check(Objects.nonNull(vtepIp), "Invalid vtep ip " + context.data()); |
| 997 | |
| 998 | return !SshUtil.exec(sshAccessInfo, |
| 999 | session -> |
| 1000 | SshUtil.hasIpAddrOnInterface(session, BRIDGE_UNDERLAY, vtepIp) |
| 1001 | && SshUtil.isIpLinkUpOnInterface(session, BRIDGE_UNDERLAY) |
| 1002 | ); |
| 1003 | } |
| 1004 | |
| 1005 | @Override |
| 1006 | public void process(WorkflowContext context) throws WorkflowException { |
| 1007 | |
| 1008 | SshAccessInfo sshAccessInfo = SshAccessInfo.valueOf(strSshAccessInfo); |
| 1009 | check(Objects.nonNull(sshAccessInfo), "Invalid ssh access info " + context.data()); |
| 1010 | |
| 1011 | NetworkAddress vtepIp = NetworkAddress.valueOf(strVtepIp); |
| 1012 | check(Objects.nonNull(vtepIp), "Invalid vtep ip " + context.data()); |
| 1013 | |
| 1014 | SshUtil.exec(sshAccessInfo, |
| 1015 | session -> { |
| 1016 | SshUtil.addIpAddrOnInterface(session, BRIDGE_UNDERLAY, vtepIp); |
| 1017 | SshUtil.setIpLinkUpOnInterface(session, BRIDGE_UNDERLAY); |
| 1018 | return ""; |
| 1019 | }); |
| 1020 | |
| 1021 | context.completed(); |
| 1022 | } |
| 1023 | } |
| 1024 | |
| 1025 | /** |
| 1026 | * Work-let class for deleting overlay bridge config. |
| 1027 | */ |
| 1028 | public static class DeleteOverlayBridgeConfig extends AbstractWorklet { |
| 1029 | |
| 1030 | @JsonDataModel(path = MODEL_MGMT_IP) |
| 1031 | String strMgmtIp; |
| 1032 | |
| 1033 | @Override |
| 1034 | public boolean isNext(WorkflowContext context) throws WorkflowException { |
| 1035 | |
| 1036 | BridgeConfig bridgeConfig = OvsUtil.getOvsdbBehaviour(context, strMgmtIp, BridgeConfig.class); |
| 1037 | |
| 1038 | Collection<BridgeDescription> bridges = bridgeConfig.getBridges(); |
| 1039 | return bridges.stream() |
| 1040 | .anyMatch(bd -> Objects.equals(bd.name(), BRIDGE_OVERLAY)); |
| 1041 | } |
| 1042 | |
| 1043 | @Override |
| 1044 | public void process(WorkflowContext context) throws WorkflowException { |
| 1045 | |
| 1046 | BridgeConfig bridgeConfig = OvsUtil.getOvsdbBehaviour(context, strMgmtIp, BridgeConfig.class); |
| 1047 | |
| 1048 | bridgeConfig.deleteBridge(BridgeName.bridgeName(BRIDGE_OVERLAY)); |
| 1049 | |
| 1050 | for (int i = 0; i < 10; i++) { |
| 1051 | if (!isNext(context)) { |
| 1052 | context.completed(); |
| 1053 | return; |
| 1054 | } |
| 1055 | sleep(50); |
| 1056 | } |
| 1057 | throw new WorkflowException("Timeout happened for removing config"); |
| 1058 | } |
| 1059 | |
| 1060 | protected void sleep(long ms) { |
| 1061 | try { |
| 1062 | Thread.sleep(ms); |
| 1063 | } catch (InterruptedException e) { |
| 1064 | e.printStackTrace(); |
| 1065 | } |
| 1066 | } |
| 1067 | } |
| 1068 | |
| 1069 | /** |
| 1070 | * Work-let class for removing overlay bridge openflow device. |
| 1071 | */ |
| 1072 | public static class RemoveOverlayBridgeOfDevice extends AbstractWorklet { |
| 1073 | |
| 1074 | @JsonDataModel(path = MODEL_MGMT_IP) |
| 1075 | String strMgmtIp; |
| 1076 | |
| 1077 | @Override |
| 1078 | public boolean isNext(WorkflowContext context) throws WorkflowException { |
| 1079 | |
| 1080 | DeviceId devId = OvsUtil.buildOfDeviceId(IpAddress.valueOf(strMgmtIp), DEVID_IDX_BRIDGE_OVERLAY); |
| 1081 | |
| 1082 | Device dev = context.getService(DeviceService.class).getDevice(devId); |
| 1083 | return dev != null; |
| 1084 | } |
| 1085 | |
| 1086 | @Override |
| 1087 | public void process(WorkflowContext context) throws WorkflowException { |
| 1088 | |
| 1089 | DeviceId devId = OvsUtil.buildOfDeviceId(IpAddress.valueOf(strMgmtIp), DEVID_IDX_BRIDGE_OVERLAY); |
| 1090 | |
| 1091 | DeviceAdminService adminService = context.getService(DeviceAdminService.class); |
| 1092 | |
| 1093 | context.waitCompletion(DeviceEvent.class, devId.toString(), |
| 1094 | () -> adminService.removeDevice(devId), |
| 1095 | TIMEOUT_DEVICE_CREATION_MS |
| 1096 | ); |
| 1097 | } |
| 1098 | |
| 1099 | @Override |
| 1100 | public boolean isCompleted(WorkflowContext context, Event event)throws WorkflowException { |
| 1101 | if (!(event instanceof DeviceEvent)) { |
| 1102 | return false; |
| 1103 | } |
| 1104 | DeviceEvent deviceEvent = (DeviceEvent) event; |
| 1105 | switch (deviceEvent.type()) { |
| 1106 | case DEVICE_REMOVED: |
| 1107 | return !isNext(context); |
| 1108 | default: |
| 1109 | return false; |
| 1110 | } |
| 1111 | } |
| 1112 | |
| 1113 | @Override |
| 1114 | public void timeout(WorkflowContext context) throws WorkflowException { |
| 1115 | if (!isNext(context)) { |
| 1116 | context.completed(); //Complete the job of worklet by timeout |
| 1117 | } else { |
| 1118 | super.timeout(context); |
| 1119 | } |
| 1120 | } |
| 1121 | } |
| 1122 | |
| 1123 | /** |
| 1124 | * Work-let class for deleting underlay bridge config. |
| 1125 | */ |
| 1126 | public static class DeleteUnderlayBridgeConfig extends AbstractWorklet { |
| 1127 | |
| 1128 | @JsonDataModel(path = MODEL_MGMT_IP) |
| 1129 | String strMgmtIp; |
| 1130 | |
| 1131 | @Override |
| 1132 | public boolean isNext(WorkflowContext context) throws WorkflowException { |
| 1133 | |
| 1134 | BridgeConfig bridgeConfig = OvsUtil.getOvsdbBehaviour(context, strMgmtIp, BridgeConfig.class); |
| 1135 | |
| 1136 | Collection<BridgeDescription> bridges = bridgeConfig.getBridges(); |
| 1137 | return bridges.stream() |
| 1138 | .anyMatch(bd -> Objects.equals(bd.name(), BRIDGE_UNDERLAY)); |
| 1139 | } |
| 1140 | |
| 1141 | @Override |
| 1142 | public void process(WorkflowContext context) throws WorkflowException { |
| 1143 | |
| 1144 | BridgeConfig bridgeConfig = OvsUtil.getOvsdbBehaviour(context, strMgmtIp, BridgeConfig.class); |
| 1145 | |
| 1146 | bridgeConfig.deleteBridge(BridgeName.bridgeName(BRIDGE_UNDERLAY)); |
| 1147 | |
| 1148 | for (int i = 0; i < 10; i++) { |
| 1149 | if (!isNext(context)) { |
| 1150 | context.completed(); |
| 1151 | return; |
| 1152 | } |
| 1153 | sleep(50); |
| 1154 | } |
| 1155 | throw new WorkflowException("Timeout happened for removing config"); |
| 1156 | } |
| 1157 | |
| 1158 | protected void sleep(long ms) { |
| 1159 | try { |
| 1160 | Thread.sleep(ms); |
| 1161 | } catch (InterruptedException e) { |
| 1162 | e.printStackTrace(); |
| 1163 | } |
| 1164 | } |
| 1165 | } |
| 1166 | |
| 1167 | /** |
| 1168 | * Work-let class for removing underlay bridge openflow device. |
| 1169 | */ |
| 1170 | public static class RemoveUnderlayBridgeOfDevice extends AbstractWorklet { |
| 1171 | |
| 1172 | @JsonDataModel(path = MODEL_MGMT_IP) |
| 1173 | String strMgmtIp; |
| 1174 | |
| 1175 | @Override |
| 1176 | public boolean isNext(WorkflowContext context) throws WorkflowException { |
| 1177 | |
| 1178 | DeviceId devId = OvsUtil.buildOfDeviceId(IpAddress.valueOf(strMgmtIp), DEVID_IDX_BRIDGE_UNDERLAY_NOVA); |
| 1179 | |
| 1180 | Device dev = context.getService(DeviceService.class).getDevice(devId); |
| 1181 | return dev != null; |
| 1182 | } |
| 1183 | |
| 1184 | @Override |
| 1185 | public void process(WorkflowContext context) throws WorkflowException { |
| 1186 | |
| 1187 | DeviceId devId = OvsUtil.buildOfDeviceId(IpAddress.valueOf(strMgmtIp), DEVID_IDX_BRIDGE_UNDERLAY_NOVA); |
| 1188 | |
| 1189 | DeviceAdminService adminService = context.getService(DeviceAdminService.class); |
| 1190 | |
| 1191 | context.waitCompletion(DeviceEvent.class, devId.toString(), |
| 1192 | () -> adminService.removeDevice(devId), |
| 1193 | TIMEOUT_DEVICE_CREATION_MS |
| 1194 | ); |
| 1195 | } |
| 1196 | |
| 1197 | @Override |
| 1198 | public boolean isCompleted(WorkflowContext context, Event event)throws WorkflowException { |
| 1199 | if (!(event instanceof DeviceEvent)) { |
| 1200 | return false; |
| 1201 | } |
| 1202 | DeviceEvent deviceEvent = (DeviceEvent) event; |
| 1203 | switch (deviceEvent.type()) { |
| 1204 | case DEVICE_REMOVED: |
| 1205 | return !isNext(context); |
| 1206 | default: |
| 1207 | return false; |
| 1208 | } |
| 1209 | } |
| 1210 | |
| 1211 | @Override |
| 1212 | public void timeout(WorkflowContext context) throws WorkflowException { |
| 1213 | if (!isNext(context)) { |
| 1214 | context.completed(); //Complete the job of worklet by timeout |
| 1215 | } else { |
| 1216 | super.timeout(context); |
| 1217 | } |
| 1218 | } |
| 1219 | } |
| 1220 | } |