[ONOS-3801] Implements L3 REST call json parser
- Nullability check for RouterCodec, RoutingInterfaceCodec, RoutingWebResource. - Copyright fixed. - externalFixedIps() method in OpenstackExternalGateway class returns the immutable Map. Change-Id: I841cc1774a074e167ffe327c6e81d3f245cc8ee0
Showing
20 changed files
with
618 additions
and
43 deletions
| ... | @@ -15,9 +15,11 @@ | ... | @@ -15,9 +15,11 @@ |
| 15 | */ | 15 | */ |
| 16 | package org.onosproject.openstackrouting; | 16 | package org.onosproject.openstackrouting; |
| 17 | 17 | ||
| 18 | +import com.google.common.collect.ImmutableMap; | ||
| 18 | import org.onlab.packet.Ip4Address; | 19 | import org.onlab.packet.Ip4Address; |
| 19 | 20 | ||
| 20 | import java.util.HashMap; | 21 | import java.util.HashMap; |
| 22 | +import java.util.Map; | ||
| 21 | 23 | ||
| 22 | /** | 24 | /** |
| 23 | * A configurable external gateway modes extension model in openstack router. | 25 | * A configurable external gateway modes extension model in openstack router. |
| ... | @@ -26,15 +28,18 @@ public final class OpenstackExternalGateway { | ... | @@ -26,15 +28,18 @@ public final class OpenstackExternalGateway { |
| 26 | 28 | ||
| 27 | private String networkId; | 29 | private String networkId; |
| 28 | private boolean enablePnat; | 30 | private boolean enablePnat; |
| 29 | - private HashMap<String, Ip4Address> externalFixedIps; | 31 | + private Map<String, Ip4Address> externalFixedIps; |
| 30 | 32 | ||
| 31 | private OpenstackExternalGateway(String networkId, boolean enablePnat, | 33 | private OpenstackExternalGateway(String networkId, boolean enablePnat, |
| 32 | - HashMap externalFixedIps) { | 34 | + Map<String, Ip4Address> externalFixedIps) { |
| 33 | this.networkId = networkId; | 35 | this.networkId = networkId; |
| 34 | this.enablePnat = enablePnat; | 36 | this.enablePnat = enablePnat; |
| 35 | this.externalFixedIps = externalFixedIps; | 37 | this.externalFixedIps = externalFixedIps; |
| 36 | } | 38 | } |
| 37 | 39 | ||
| 40 | + public static OpenstackExternalGateway.Builder builder() { | ||
| 41 | + return new Builder(); | ||
| 42 | + } | ||
| 38 | /** | 43 | /** |
| 39 | * Returns network ID. | 44 | * Returns network ID. |
| 40 | * | 45 | * |
| ... | @@ -53,13 +58,17 @@ public final class OpenstackExternalGateway { | ... | @@ -53,13 +58,17 @@ public final class OpenstackExternalGateway { |
| 53 | return enablePnat; | 58 | return enablePnat; |
| 54 | } | 59 | } |
| 55 | 60 | ||
| 61 | + public Map<String, Ip4Address> externalFixedIps() { | ||
| 62 | + return ImmutableMap.copyOf(externalFixedIps); | ||
| 63 | + } | ||
| 64 | + | ||
| 56 | /** | 65 | /** |
| 57 | * An Openstack External Gateway Builder class. | 66 | * An Openstack External Gateway Builder class. |
| 58 | */ | 67 | */ |
| 59 | public static final class Builder { | 68 | public static final class Builder { |
| 60 | private String networkId; | 69 | private String networkId; |
| 61 | private boolean enablePnat; | 70 | private boolean enablePnat; |
| 62 | - private HashMap<String, Ip4Address> externalFixedIps; | 71 | + private Map<String, Ip4Address> externalFixedIps; |
| 63 | 72 | ||
| 64 | Builder() { | 73 | Builder() { |
| 65 | externalFixedIps = new HashMap<>(); | 74 | externalFixedIps = new HashMap<>(); |
| ... | @@ -90,11 +99,12 @@ public final class OpenstackExternalGateway { | ... | @@ -90,11 +99,12 @@ public final class OpenstackExternalGateway { |
| 90 | /** | 99 | /** |
| 91 | * Sets external fixed IP address information. | 100 | * Sets external fixed IP address information. |
| 92 | * | 101 | * |
| 93 | - * @param externalFixedIPs External fixed IP information | 102 | + * @param externalFixedIps External fixed IP information |
| 94 | * @return Builder object | 103 | * @return Builder object |
| 95 | */ | 104 | */ |
| 96 | - public Builder externalFixedIps(HashMap<String, Ip4Address> externalFixedIPs) { | 105 | + |
| 97 | - this.externalFixedIps.putAll(externalFixedIPs); | 106 | + public Builder externalFixedIps(Map<String, Ip4Address> externalFixedIps) { |
| 107 | + this.externalFixedIps.putAll(externalFixedIps); | ||
| 98 | return this; | 108 | return this; |
| 99 | } | 109 | } |
| 100 | 110 | ... | ... |
| ... | @@ -44,6 +44,10 @@ public final class OpenstackRouter { | ... | @@ -44,6 +44,10 @@ public final class OpenstackRouter { |
| 44 | 44 | ||
| 45 | } | 45 | } |
| 46 | 46 | ||
| 47 | + public static OpenstackRouter.Builder builder() { | ||
| 48 | + return new Builder(); | ||
| 49 | + } | ||
| 50 | + | ||
| 47 | /** | 51 | /** |
| 48 | * Returns tenant ID. | 52 | * Returns tenant ID. |
| 49 | * | 53 | * | ... | ... |
| ... | @@ -124,10 +124,9 @@ public class OpenstackRoutingManager implements OpenstackRoutingService { | ... | @@ -124,10 +124,9 @@ public class OpenstackRoutingManager implements OpenstackRoutingService { |
| 124 | } | 124 | } |
| 125 | 125 | ||
| 126 | @Override | 126 | @Override |
| 127 | - public void deleteRouterInterface(String id) { | 127 | + public void removeRouterInterface(OpenstackRouterInterface openstackRouterInterface) { |
| 128 | 128 | ||
| 129 | } | 129 | } |
| 130 | - | ||
| 131 | private class InternalPacketProcessor implements PacketProcessor { | 130 | private class InternalPacketProcessor implements PacketProcessor { |
| 132 | 131 | ||
| 133 | @Override | 132 | @Override | ... | ... |
| ... | @@ -77,11 +77,11 @@ public interface OpenstackRoutingService { | ... | @@ -77,11 +77,11 @@ public interface OpenstackRoutingService { |
| 77 | void updateRouterInterface(OpenstackRouterInterface openstackRouterInterface); | 77 | void updateRouterInterface(OpenstackRouterInterface openstackRouterInterface); |
| 78 | 78 | ||
| 79 | /** | 79 | /** |
| 80 | - * Removes flow rules corresponding to the router information deleted by Openstack. | 80 | + * Removes flow rules corresponding to the router information removed by Openstack. |
| 81 | * | 81 | * |
| 82 | - * @param id Deleted router`s ID | 82 | + * @param openstackRouterInterface Router information |
| 83 | */ | 83 | */ |
| 84 | - void deleteRouterInterface(String id); | 84 | + void removeRouterInterface(OpenstackRouterInterface openstackRouterInterface); |
| 85 | 85 | ||
| 86 | 86 | ||
| 87 | } | 87 | } | ... | ... |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2014-2015 Open Networking Laboratory | 2 | + * Copyright 2015 Open Networking Laboratory |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| ... | @@ -13,30 +13,29 @@ | ... | @@ -13,30 +13,29 @@ |
| 13 | * See the License for the specific language governing permissions and | 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. | 14 | * limitations under the License. |
| 15 | */ | 15 | */ |
| 16 | -package org.onosproject.openstackswitching.impl; | 16 | +package org.onosproject.openstackswitching; |
| 17 | 17 | ||
| 18 | import org.onlab.packet.Ip4Address; | 18 | import org.onlab.packet.Ip4Address; |
| 19 | import org.onlab.packet.MacAddress; | 19 | import org.onlab.packet.MacAddress; |
| 20 | import org.onosproject.net.DeviceId; | 20 | import org.onosproject.net.DeviceId; |
| 21 | - | ||
| 22 | import static com.google.common.base.Preconditions.checkNotNull; | 21 | import static com.google.common.base.Preconditions.checkNotNull; |
| 23 | 22 | ||
| 24 | /** | 23 | /** |
| 25 | * Contains OpenstackPort Information. | 24 | * Contains OpenstackPort Information. |
| 26 | */ | 25 | */ |
| 27 | -public final class OpenstackPortInfo { | 26 | +public class OpenstackPortInfo { |
| 28 | - | ||
| 29 | private final Ip4Address hostIp; | 27 | private final Ip4Address hostIp; |
| 30 | - private final DeviceId deviceId; | ||
| 31 | private final MacAddress hostMac; | 28 | private final MacAddress hostMac; |
| 29 | + private final DeviceId deviceId; | ||
| 32 | private final long vni; | 30 | private final long vni; |
| 31 | + private final Ip4Address gatewayIP; | ||
| 33 | 32 | ||
| 34 | - public OpenstackPortInfo(Ip4Address hostIp, MacAddress hostMac, DeviceId deviceId, | 33 | + public OpenstackPortInfo(Ip4Address hostIp, MacAddress hostMac, DeviceId deviceId, long vni, Ip4Address gatewayIP) { |
| 35 | - long vni) { | ||
| 36 | this.hostIp = hostIp; | 34 | this.hostIp = hostIp; |
| 37 | this.hostMac = hostMac; | 35 | this.hostMac = hostMac; |
| 38 | this.deviceId = deviceId; | 36 | this.deviceId = deviceId; |
| 39 | this.vni = vni; | 37 | this.vni = vni; |
| 38 | + this.gatewayIP = gatewayIP; | ||
| 40 | } | 39 | } |
| 41 | 40 | ||
| 42 | public Ip4Address ip() { | 41 | public Ip4Address ip() { |
| ... | @@ -55,6 +54,10 @@ public final class OpenstackPortInfo { | ... | @@ -55,6 +54,10 @@ public final class OpenstackPortInfo { |
| 55 | return vni; | 54 | return vni; |
| 56 | } | 55 | } |
| 57 | 56 | ||
| 57 | + public Ip4Address gatewayIP() { | ||
| 58 | + return gatewayIP; | ||
| 59 | + } | ||
| 60 | + | ||
| 58 | public static OpenstackPortInfo.Builder builder() { | 61 | public static OpenstackPortInfo.Builder builder() { |
| 59 | return new Builder(); | 62 | return new Builder(); |
| 60 | } | 63 | } |
| ... | @@ -64,6 +67,12 @@ public final class OpenstackPortInfo { | ... | @@ -64,6 +67,12 @@ public final class OpenstackPortInfo { |
| 64 | private MacAddress hostMac; | 67 | private MacAddress hostMac; |
| 65 | private DeviceId deviceId; | 68 | private DeviceId deviceId; |
| 66 | private long vni; | 69 | private long vni; |
| 70 | + private Ip4Address gatewayIP; | ||
| 71 | + | ||
| 72 | + public Builder setGatewayIP(Ip4Address gatewayIP) { | ||
| 73 | + this.gatewayIP = checkNotNull(gatewayIP, "gatewayIP cannot be null"); | ||
| 74 | + return this; | ||
| 75 | + } | ||
| 67 | 76 | ||
| 68 | public Builder setHostIp(Ip4Address hostIp) { | 77 | public Builder setHostIp(Ip4Address hostIp) { |
| 69 | this.hostIp = checkNotNull(hostIp, "hostIp cannot be null"); | 78 | this.hostIp = checkNotNull(hostIp, "hostIp cannot be null"); |
| ... | @@ -94,5 +103,6 @@ public final class OpenstackPortInfo { | ... | @@ -94,5 +103,6 @@ public final class OpenstackPortInfo { |
| 94 | hostMac = builder.hostMac; | 103 | hostMac = builder.hostMac; |
| 95 | deviceId = builder.deviceId; | 104 | deviceId = builder.deviceId; |
| 96 | vni = builder.vni; | 105 | vni = builder.vni; |
| 106 | + gatewayIP = builder.gatewayIP; | ||
| 97 | } | 107 | } |
| 98 | } | 108 | } | ... | ... |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2015 Open Networking Laboratory | 2 | + * Copyright 2015-2016 Open Networking Laboratory |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| ... | @@ -16,6 +16,8 @@ | ... | @@ -16,6 +16,8 @@ |
| 16 | package org.onosproject.openstackswitching; | 16 | package org.onosproject.openstackswitching; |
| 17 | 17 | ||
| 18 | import org.onosproject.net.Port; | 18 | import org.onosproject.net.Port; |
| 19 | +import org.onosproject.openstackrouting.OpenstackRouter; | ||
| 20 | +import org.onosproject.openstackrouting.OpenstackRouterInterface; | ||
| 19 | 21 | ||
| 20 | import java.util.Collection; | 22 | import java.util.Collection; |
| 21 | 23 | ||
| ... | @@ -68,6 +70,12 @@ public interface OpenstackSwitchingService { | ... | @@ -68,6 +70,12 @@ public interface OpenstackSwitchingService { |
| 68 | Collection<OpenstackPort> ports(String networkId); | 70 | Collection<OpenstackPort> ports(String networkId); |
| 69 | 71 | ||
| 70 | /** | 72 | /** |
| 73 | + * Returns port information list. | ||
| 74 | + * | ||
| 75 | + * @return port information list | ||
| 76 | + */ | ||
| 77 | + Collection<OpenstackPort> ports(); | ||
| 78 | + /** | ||
| 71 | * Returns port information for the port given. | 79 | * Returns port information for the port given. |
| 72 | * | 80 | * |
| 73 | * @param port port reference | 81 | * @param port port reference |
| ... | @@ -99,4 +107,68 @@ public interface OpenstackSwitchingService { | ... | @@ -99,4 +107,68 @@ public interface OpenstackSwitchingService { |
| 99 | * @return subnet information, or null if not present | 107 | * @return subnet information, or null if not present |
| 100 | */ | 108 | */ |
| 101 | OpenstackSubnet subnet(String subnetId); | 109 | OpenstackSubnet subnet(String subnetId); |
| 110 | + | ||
| 111 | + /** | ||
| 112 | + * Sends the created router information to OpenstackRouting service. | ||
| 113 | + * | ||
| 114 | + * @param openstackRouter Router Information | ||
| 115 | + */ | ||
| 116 | + void createRouter(OpenstackRouter openstackRouter); | ||
| 117 | + /** | ||
| 118 | + * Sends the updated router information to OpenstackRouting service. | ||
| 119 | + * | ||
| 120 | + * @param routerId Router ID | ||
| 121 | + */ | ||
| 122 | + void updateRouter(String routerId); | ||
| 123 | + /** | ||
| 124 | + * Sends the removed router information to OpenstackRouting service. | ||
| 125 | + * | ||
| 126 | + * @param routerId Router ID | ||
| 127 | + */ | ||
| 128 | + void deleteRouter(String routerId); | ||
| 129 | + | ||
| 130 | + /** | ||
| 131 | + * Sends the updated router interface information to OpenstackRouting service. | ||
| 132 | + * | ||
| 133 | + * @param openstackRouterInterface Router interface information | ||
| 134 | + */ | ||
| 135 | + void updateRouterInterface(OpenstackRouterInterface openstackRouterInterface); | ||
| 136 | + | ||
| 137 | + /** | ||
| 138 | + * Sends the removed router interface information to OpenstackRouting service. | ||
| 139 | + * | ||
| 140 | + * @param openstackRouterInterface Router interface information | ||
| 141 | + */ | ||
| 142 | + void removeRouterInterface(OpenstackRouterInterface openstackRouterInterface); | ||
| 143 | + | ||
| 144 | + /** | ||
| 145 | + * Returns the router information list. | ||
| 146 | + * | ||
| 147 | + * @return router information list | ||
| 148 | + */ | ||
| 149 | + Collection<OpenstackRouter> routers(); | ||
| 150 | + | ||
| 151 | + /** | ||
| 152 | + * Returns the router information for the router ID given. | ||
| 153 | + * | ||
| 154 | + * @param routerId router ID | ||
| 155 | + * @return router information | ||
| 156 | + */ | ||
| 157 | + OpenstackRouter router(String routerId); | ||
| 158 | + | ||
| 159 | + /** | ||
| 160 | + * Returns the OpensatckPortInfo list. | ||
| 161 | + * | ||
| 162 | + * @return OpensatckPortInfo list | ||
| 163 | + */ | ||
| 164 | + Collection<OpenstackPortInfo> portInfos(); | ||
| 165 | + | ||
| 166 | + /** | ||
| 167 | + * Returns the MacAddress for physical router. | ||
| 168 | + * | ||
| 169 | + * @return physical router mac | ||
| 170 | + */ | ||
| 171 | + String physicalRouterMac(); | ||
| 172 | + | ||
| 173 | + | ||
| 102 | } | 174 | } | ... | ... |
| 1 | <?xml version="1.0" encoding="UTF-8" standalone="yes"?> | 1 | <?xml version="1.0" encoding="UTF-8" standalone="yes"?> |
| 2 | <!-- | 2 | <!-- |
| 3 | - ~ Copyright 2015 Open Networking Laboratory | 3 | + ~ Copyright 2015-16 Open Networking Laboratory |
| 4 | ~ | 4 | ~ |
| 5 | ~ Licensed under the Apache License, Version 2.0 (the "License"); | 5 | ~ Licensed under the Apache License, Version 2.0 (the "License"); |
| 6 | ~ you may not use this file except in compliance with the License. | 6 | ~ you may not use this file except in compliance with the License. |
| ... | @@ -22,6 +22,7 @@ | ... | @@ -22,6 +22,7 @@ |
| 22 | <bundle>mvn:${project.groupId}/onos-app-openstackswitching/${project.version}</bundle> | 22 | <bundle>mvn:${project.groupId}/onos-app-openstackswitching/${project.version}</bundle> |
| 23 | <bundle>mvn:${project.groupId}/onos-app-dhcp-api/${project.version}</bundle> | 23 | <bundle>mvn:${project.groupId}/onos-app-dhcp-api/${project.version}</bundle> |
| 24 | <bundle>mvn:${project.groupId}/onos-app-dhcp/${project.version}</bundle> | 24 | <bundle>mvn:${project.groupId}/onos-app-dhcp/${project.version}</bundle> |
| 25 | + <bundle>mvn:${project.groupId}/onos-app-openstackrouting/${project.version}</bundle> | ||
| 25 | <bundle>mvn:com.sun.jersey/jersey-client/1.19</bundle> | 26 | <bundle>mvn:com.sun.jersey/jersey-client/1.19</bundle> |
| 26 | </feature> | 27 | </feature> |
| 27 | </features> | 28 | </features> | ... | ... |
| 1 | /* | 1 | /* |
| 2 | -* Copyright 2015 Open Networking Laboratory | 2 | +* Copyright 2015-2016 Open Networking Laboratory |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| ... | @@ -28,9 +28,11 @@ import org.onosproject.net.packet.DefaultOutboundPacket; | ... | @@ -28,9 +28,11 @@ import org.onosproject.net.packet.DefaultOutboundPacket; |
| 28 | import org.onosproject.net.packet.InboundPacket; | 28 | import org.onosproject.net.packet.InboundPacket; |
| 29 | import org.onosproject.net.packet.PacketService; | 29 | import org.onosproject.net.packet.PacketService; |
| 30 | import org.onosproject.openstackswitching.OpenstackPort; | 30 | import org.onosproject.openstackswitching.OpenstackPort; |
| 31 | +import org.onosproject.openstackswitching.OpenstackPortInfo; | ||
| 31 | import org.slf4j.Logger; | 32 | import org.slf4j.Logger; |
| 32 | import org.slf4j.LoggerFactory; | 33 | import org.slf4j.LoggerFactory; |
| 33 | import java.nio.ByteBuffer; | 34 | import java.nio.ByteBuffer; |
| 35 | +import java.util.Collection; | ||
| 34 | 36 | ||
| 35 | import static com.google.common.base.Preconditions.checkNotNull; | 37 | import static com.google.common.base.Preconditions.checkNotNull; |
| 36 | 38 | ||
| ... | @@ -41,6 +43,7 @@ public class OpenstackArpHandler { | ... | @@ -41,6 +43,7 @@ public class OpenstackArpHandler { |
| 41 | 43 | ||
| 42 | private static Logger log = LoggerFactory | 44 | private static Logger log = LoggerFactory |
| 43 | .getLogger(OpenstackArpHandler.class); | 45 | .getLogger(OpenstackArpHandler.class); |
| 46 | + private static final MacAddress GATEWAY_MAC = MacAddress.valueOf("1f:1f:1f:1f:1f:1f"); | ||
| 44 | private PacketService packetService; | 47 | private PacketService packetService; |
| 45 | private OpenstackRestHandler restHandler; | 48 | private OpenstackRestHandler restHandler; |
| 46 | private HostService hostService; | 49 | private HostService hostService; |
| ... | @@ -66,7 +69,7 @@ public class OpenstackArpHandler { | ... | @@ -66,7 +69,7 @@ public class OpenstackArpHandler { |
| 66 | * | 69 | * |
| 67 | * @param pkt ARP request packet | 70 | * @param pkt ARP request packet |
| 68 | */ | 71 | */ |
| 69 | - public void processPacketIn(InboundPacket pkt) { | 72 | + public void processPacketIn(InboundPacket pkt, Collection<OpenstackPortInfo> openstackPortInfoCollection) { |
| 70 | Ethernet ethRequest = pkt.parsed(); | 73 | Ethernet ethRequest = pkt.parsed(); |
| 71 | ARP arp = (ARP) ethRequest.getPayload(); | 74 | ARP arp = (ARP) ethRequest.getPayload(); |
| 72 | 75 | ||
| ... | @@ -74,10 +77,21 @@ public class OpenstackArpHandler { | ... | @@ -74,10 +77,21 @@ public class OpenstackArpHandler { |
| 74 | return; | 77 | return; |
| 75 | } | 78 | } |
| 76 | 79 | ||
| 80 | + IpAddress sourceIp = Ip4Address.valueOf(arp.getSenderProtocolAddress()); | ||
| 81 | + MacAddress srcMac = MacAddress.valueOf(arp.getSenderHardwareAddress()); | ||
| 82 | + OpenstackPortInfo portInfo = openstackPortInfoCollection.stream() | ||
| 83 | + .filter(p -> p.ip().equals(sourceIp) && p.mac().equals(srcMac)).findFirst().orElse(null); | ||
| 77 | IpAddress targetIp = Ip4Address.valueOf(arp.getTargetProtocolAddress()); | 84 | IpAddress targetIp = Ip4Address.valueOf(arp.getTargetProtocolAddress()); |
| 78 | - MacAddress dstMac = getMacFromHostService(targetIp); | 85 | + |
| 79 | - if (dstMac == null) { | 86 | + MacAddress dstMac; |
| 80 | - dstMac = getMacFromOpenstack(targetIp); | 87 | + |
| 88 | + if (targetIp.equals(portInfo == null ? null : portInfo.gatewayIP())) { | ||
| 89 | + dstMac = GATEWAY_MAC; | ||
| 90 | + } else { | ||
| 91 | + dstMac = getMacFromHostService(targetIp); | ||
| 92 | + if (dstMac == null) { | ||
| 93 | + dstMac = getMacFromOpenstack(targetIp); | ||
| 94 | + } | ||
| 81 | } | 95 | } |
| 82 | 96 | ||
| 83 | if (dstMac == null) { | 97 | if (dstMac == null) { | ... | ... |
| ... | @@ -21,12 +21,14 @@ import com.fasterxml.jackson.databind.node.ObjectNode; | ... | @@ -21,12 +21,14 @@ import com.fasterxml.jackson.databind.node.ObjectNode; |
| 21 | import com.google.common.collect.Lists; | 21 | import com.google.common.collect.Lists; |
| 22 | import com.sun.jersey.api.client.Client; | 22 | import com.sun.jersey.api.client.Client; |
| 23 | import com.sun.jersey.api.client.WebResource; | 23 | import com.sun.jersey.api.client.WebResource; |
| 24 | +import org.onosproject.openstackrouting.OpenstackRouter; | ||
| 24 | import org.onosproject.openstackswitching.OpenstackNetwork; | 25 | import org.onosproject.openstackswitching.OpenstackNetwork; |
| 25 | import org.onosproject.openstackswitching.OpenstackPort; | 26 | import org.onosproject.openstackswitching.OpenstackPort; |
| 26 | import org.onosproject.openstackswitching.OpenstackSubnet; | 27 | import org.onosproject.openstackswitching.OpenstackSubnet; |
| 27 | import org.onosproject.openstackswitching.web.OpenstackNetworkCodec; | 28 | import org.onosproject.openstackswitching.web.OpenstackNetworkCodec; |
| 28 | import org.onosproject.openstackswitching.web.OpenstackPortCodec; | 29 | import org.onosproject.openstackswitching.web.OpenstackPortCodec; |
| 29 | import org.onosproject.openstackswitching.web.OpenstackSecurityGroupCodec; | 30 | import org.onosproject.openstackswitching.web.OpenstackSecurityGroupCodec; |
| 31 | +import org.onosproject.openstackswitching.web.OpenstackRouterCodec; | ||
| 30 | import org.onosproject.openstackswitching.web.OpenstackSubnetCodec; | 32 | import org.onosproject.openstackswitching.web.OpenstackSubnetCodec; |
| 31 | import org.slf4j.Logger; | 33 | import org.slf4j.Logger; |
| 32 | import javax.ws.rs.core.MediaType; | 34 | import javax.ws.rs.core.MediaType; |
| ... | @@ -51,6 +53,7 @@ public class OpenstackRestHandler { | ... | @@ -51,6 +53,7 @@ public class OpenstackRestHandler { |
| 51 | private static final String URI_SECURITY_GROUPS = "security-groups"; | 53 | private static final String URI_SECURITY_GROUPS = "security-groups"; |
| 52 | private static final String URI_TOKENS = "tokens"; | 54 | private static final String URI_TOKENS = "tokens"; |
| 53 | 55 | ||
| 56 | + private static final String PATH_ROUTERS = "routers"; | ||
| 54 | private static final String PATH_NETWORKS = "networks"; | 57 | private static final String PATH_NETWORKS = "networks"; |
| 55 | private static final String PATH_PORTS = "ports"; | 58 | private static final String PATH_PORTS = "ports"; |
| 56 | private static final String PATH_SUBNETS = "subnets"; | 59 | private static final String PATH_SUBNETS = "subnets"; |
| ... | @@ -137,6 +140,30 @@ public class OpenstackRestHandler { | ... | @@ -137,6 +140,30 @@ public class OpenstackRestHandler { |
| 137 | return openstackPorts; | 140 | return openstackPorts; |
| 138 | } | 141 | } |
| 139 | 142 | ||
| 143 | + public Collection<OpenstackRouter> getRouters() { | ||
| 144 | + WebResource.Builder builder = getClientBuilder(neutronUrl + PATH_ROUTERS); | ||
| 145 | + String response = builder.accept(MediaType.APPLICATION_JSON_TYPE). | ||
| 146 | + header(HEADER_AUTH_TOKEN, getToken()).get(String.class); | ||
| 147 | + | ||
| 148 | + ObjectMapper mapper = new ObjectMapper(); | ||
| 149 | + List<OpenstackRouter> openstackRouters = Lists.newArrayList(); | ||
| 150 | + | ||
| 151 | + try { | ||
| 152 | + ObjectNode node = (ObjectNode) mapper.readTree(response); | ||
| 153 | + ArrayNode routerList = (ArrayNode) node.path(PATH_ROUTERS); | ||
| 154 | + OpenstackRouterCodec openstackRouterCodec = new OpenstackRouterCodec(); | ||
| 155 | + routerList.forEach(r -> openstackRouters | ||
| 156 | + .add(openstackRouterCodec.decode((ObjectNode) r, null))); | ||
| 157 | + } catch (IOException e) { | ||
| 158 | + log.warn("getRouters()", e); | ||
| 159 | + } | ||
| 160 | + | ||
| 161 | + log.debug("router response:" + response); | ||
| 162 | + openstackRouters.forEach(r -> log.debug("router ID: {}", r.id())); | ||
| 163 | + | ||
| 164 | + return openstackRouters; | ||
| 165 | + } | ||
| 166 | + | ||
| 140 | /** | 167 | /** |
| 141 | * Returns Subnet information in Neutron. | 168 | * Returns Subnet information in Neutron. |
| 142 | * | 169 | * | ... | ... |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2014 Open Networking Laboratory | 2 | + * Copyright 2015-2016 Open Networking Laboratory |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| ... | @@ -28,6 +28,7 @@ public class OpenstackSwitchingConfig extends Config<ApplicationId> { | ... | @@ -28,6 +28,7 @@ public class OpenstackSwitchingConfig extends Config<ApplicationId> { |
| 28 | public static final String KEYSTONE_SERVER = "keystone_server"; | 28 | public static final String KEYSTONE_SERVER = "keystone_server"; |
| 29 | public static final String USER_NAME = "user_name"; | 29 | public static final String USER_NAME = "user_name"; |
| 30 | public static final String PASSWORD = "password"; | 30 | public static final String PASSWORD = "password"; |
| 31 | + public static final String PHYSICAL_ROUTER_MAC = "physicalRouterMac"; | ||
| 31 | 32 | ||
| 32 | /** | 33 | /** |
| 33 | * Returns the flag whether the app pushes flows or not. | 34 | * Returns the flag whether the app pushes flows or not. |
| ... | @@ -76,6 +77,14 @@ public class OpenstackSwitchingConfig extends Config<ApplicationId> { | ... | @@ -76,6 +77,14 @@ public class OpenstackSwitchingConfig extends Config<ApplicationId> { |
| 76 | } | 77 | } |
| 77 | 78 | ||
| 78 | /** | 79 | /** |
| 80 | + * Returns the MacAddress for physical router. | ||
| 81 | + * | ||
| 82 | + * @return physical router mac | ||
| 83 | + */ | ||
| 84 | + public String physicalRouterMac() { | ||
| 85 | + return get(PHYSICAL_ROUTER_MAC, ""); | ||
| 86 | + } | ||
| 87 | + /** | ||
| 79 | * Sets the flag whether the app pushes flows or not. | 88 | * Sets the flag whether the app pushes flows or not. |
| 80 | * | 89 | * |
| 81 | * @param flag the flag whether the app pushes flows or not | 90 | * @param flag the flag whether the app pushes flows or not | ... | ... |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2015 Open Networking Laboratory | 2 | + * Copyright 2015-2016 Open Networking Laboratory |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| ... | @@ -50,8 +50,12 @@ import org.onosproject.net.packet.InboundPacket; | ... | @@ -50,8 +50,12 @@ import org.onosproject.net.packet.InboundPacket; |
| 50 | import org.onosproject.net.packet.PacketContext; | 50 | import org.onosproject.net.packet.PacketContext; |
| 51 | import org.onosproject.net.packet.PacketProcessor; | 51 | import org.onosproject.net.packet.PacketProcessor; |
| 52 | import org.onosproject.net.packet.PacketService; | 52 | import org.onosproject.net.packet.PacketService; |
| 53 | +import org.onosproject.openstackrouting.OpenstackRouter; | ||
| 54 | +import org.onosproject.openstackrouting.OpenstackRouterInterface; | ||
| 55 | +import org.onosproject.openstackrouting.OpenstackRoutingService; | ||
| 53 | import org.onosproject.openstackswitching.OpenstackNetwork; | 56 | import org.onosproject.openstackswitching.OpenstackNetwork; |
| 54 | import org.onosproject.openstackswitching.OpenstackPort; | 57 | import org.onosproject.openstackswitching.OpenstackPort; |
| 58 | +import org.onosproject.openstackswitching.OpenstackPortInfo; | ||
| 55 | import org.onosproject.openstackswitching.OpenstackSubnet; | 59 | import org.onosproject.openstackswitching.OpenstackSubnet; |
| 56 | import org.onosproject.openstackswitching.OpenstackSwitchingService; | 60 | import org.onosproject.openstackswitching.OpenstackSwitchingService; |
| 57 | import org.slf4j.Logger; | 61 | import org.slf4j.Logger; |
| ... | @@ -103,11 +107,14 @@ public class OpenstackSwitchingManager implements OpenstackSwitchingService { | ... | @@ -103,11 +107,14 @@ public class OpenstackSwitchingManager implements OpenstackSwitchingService { |
| 103 | @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | 107 | @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) |
| 104 | protected DriverService driverService; | 108 | protected DriverService driverService; |
| 105 | 109 | ||
| 110 | + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | ||
| 111 | + protected OpenstackRoutingService openstackRoutingService; | ||
| 112 | + | ||
| 106 | public static final String PORTNAME_PREFIX_VM = "tap"; | 113 | public static final String PORTNAME_PREFIX_VM = "tap"; |
| 107 | public static final String PORTNAME_PREFIX_ROUTER = "qr-"; | 114 | public static final String PORTNAME_PREFIX_ROUTER = "qr-"; |
| 108 | public static final String PORTNAME_PREFIX_TUNNEL = "vxlan"; | 115 | public static final String PORTNAME_PREFIX_TUNNEL = "vxlan"; |
| 109 | public static final String PORTNAME = "portName"; | 116 | public static final String PORTNAME = "portName"; |
| 110 | - | 117 | + private static final String ROUTER_INTERFACE = "network:router_interface"; |
| 111 | public static final String DEVICE_OWNER_GATEWAY = "network:router_gateway"; | 118 | public static final String DEVICE_OWNER_GATEWAY = "network:router_gateway"; |
| 112 | 119 | ||
| 113 | private ApplicationId appId; | 120 | private ApplicationId appId; |
| ... | @@ -116,6 +123,7 @@ public class OpenstackSwitchingManager implements OpenstackSwitchingService { | ... | @@ -116,6 +123,7 @@ public class OpenstackSwitchingManager implements OpenstackSwitchingService { |
| 116 | private Ip4Address keystoneServer; | 123 | private Ip4Address keystoneServer; |
| 117 | private String userName; | 124 | private String userName; |
| 118 | private String password; | 125 | private String password; |
| 126 | + private String physicalRouterMac; | ||
| 119 | private OpenstackArpHandler arpHandler; | 127 | private OpenstackArpHandler arpHandler; |
| 120 | private OpenstackRestHandler restHandler; | 128 | private OpenstackRestHandler restHandler; |
| 121 | 129 | ||
| ... | @@ -139,7 +147,6 @@ public class OpenstackSwitchingManager implements OpenstackSwitchingService { | ... | @@ -139,7 +147,6 @@ public class OpenstackSwitchingManager implements OpenstackSwitchingService { |
| 139 | } | 147 | } |
| 140 | ); | 148 | ); |
| 141 | 149 | ||
| 142 | - | ||
| 143 | private Map<String, OpenstackPortInfo> openstackPortInfoMap = Maps.newHashMap(); | 150 | private Map<String, OpenstackPortInfo> openstackPortInfoMap = Maps.newHashMap(); |
| 144 | 151 | ||
| 145 | @Activate | 152 | @Activate |
| ... | @@ -172,9 +179,12 @@ public class OpenstackSwitchingManager implements OpenstackSwitchingService { | ... | @@ -172,9 +179,12 @@ public class OpenstackSwitchingManager implements OpenstackSwitchingService { |
| 172 | 179 | ||
| 173 | @Override | 180 | @Override |
| 174 | public void createPorts(OpenstackPort openstackPort) { | 181 | public void createPorts(OpenstackPort openstackPort) { |
| 175 | - if (!openstackPort.fixedIps().isEmpty() | 182 | + |
| 176 | - && !openstackPort.deviceOwner().equals(DEVICE_OWNER_GATEWAY)) { | 183 | + if (!openstackPort.deviceOwner().equals(ROUTER_INTERFACE) |
| 177 | - registerDhcpInfo(openstackPort); | 184 | + && !openstackPort.deviceOwner().equals(DEVICE_OWNER_GATEWAY)) { |
| 185 | + if (!openstackPort.fixedIps().isEmpty()) { | ||
| 186 | + registerDhcpInfo(openstackPort); | ||
| 187 | + } | ||
| 178 | } | 188 | } |
| 179 | 189 | ||
| 180 | if (!openstackPort.securityGroups().isEmpty()) { | 190 | if (!openstackPort.securityGroups().isEmpty()) { |
| ... | @@ -236,6 +246,11 @@ public class OpenstackSwitchingManager implements OpenstackSwitchingService { | ... | @@ -236,6 +246,11 @@ public class OpenstackSwitchingManager implements OpenstackSwitchingService { |
| 236 | } | 246 | } |
| 237 | 247 | ||
| 238 | @Override | 248 | @Override |
| 249 | + public Collection<OpenstackPort> ports() { | ||
| 250 | + return restHandler.getPorts(); | ||
| 251 | + } | ||
| 252 | + | ||
| 253 | + @Override | ||
| 239 | public OpenstackPort port(Port port) { | 254 | public OpenstackPort port(Port port) { |
| 240 | Collection<OpenstackPort> ports = restHandler.getPorts(); | 255 | Collection<OpenstackPort> ports = restHandler.getPorts(); |
| 241 | String uuid = port.annotations().value(PORTNAME).substring(3); | 256 | String uuid = port.annotations().value(PORTNAME).substring(3); |
| ... | @@ -291,6 +306,57 @@ public class OpenstackSwitchingManager implements OpenstackSwitchingService { | ... | @@ -291,6 +306,57 @@ public class OpenstackSwitchingManager implements OpenstackSwitchingService { |
| 291 | } | 306 | } |
| 292 | } | 307 | } |
| 293 | 308 | ||
| 309 | + @Override | ||
| 310 | + public void createRouter(OpenstackRouter openstackRouter) { | ||
| 311 | + openstackRoutingService.createRouter(openstackRouter); | ||
| 312 | + } | ||
| 313 | + @Override | ||
| 314 | + public void updateRouter(String routerId) { | ||
| 315 | + openstackRoutingService.updateRouter(router(routerId)); | ||
| 316 | + } | ||
| 317 | + | ||
| 318 | + @Override | ||
| 319 | + public void removeRouterInterface(OpenstackRouterInterface openstackRouterInterface) { | ||
| 320 | + openstackRoutingService.removeRouterInterface(openstackRouterInterface); | ||
| 321 | + } | ||
| 322 | + @Override | ||
| 323 | + public void deleteRouter(String id) { | ||
| 324 | + openstackRoutingService.deleteRouter(id); | ||
| 325 | + } | ||
| 326 | + | ||
| 327 | + @Override | ||
| 328 | + public void updateRouterInterface(OpenstackRouterInterface openstackRouterInterface) { | ||
| 329 | + openstackRoutingService.updateRouterInterface(openstackRouterInterface); | ||
| 330 | + } | ||
| 331 | + | ||
| 332 | + @Override | ||
| 333 | + public OpenstackRouter router(String routerId) { | ||
| 334 | + Collection<OpenstackRouter> openstackRouters = restHandler.getRouters(); | ||
| 335 | + try { | ||
| 336 | + return openstackRouters.stream() | ||
| 337 | + .filter(r -> r.id().equals(routerId)) | ||
| 338 | + .findAny().get(); | ||
| 339 | + } catch (NoSuchElementException e) { | ||
| 340 | + log.warn("There is no router info for subnet ID {}", routerId); | ||
| 341 | + return null; | ||
| 342 | + } | ||
| 343 | + } | ||
| 344 | + | ||
| 345 | + @Override | ||
| 346 | + public Collection<OpenstackRouter> routers() { | ||
| 347 | + return restHandler.getRouters(); | ||
| 348 | + } | ||
| 349 | + | ||
| 350 | + @Override | ||
| 351 | + public Collection<OpenstackPortInfo> portInfos() { | ||
| 352 | + return openstackPortInfoMap.values(); | ||
| 353 | + } | ||
| 354 | + | ||
| 355 | + @Override | ||
| 356 | + public String physicalRouterMac() { | ||
| 357 | + return physicalRouterMac; | ||
| 358 | + } | ||
| 359 | + | ||
| 294 | private void processDeviceAdded(Device device) { | 360 | private void processDeviceAdded(Device device) { |
| 295 | log.debug("device {} is added", device.id()); | 361 | log.debug("device {} is added", device.id()); |
| 296 | } | 362 | } |
| ... | @@ -301,8 +367,11 @@ public class OpenstackSwitchingManager implements OpenstackSwitchingService { | ... | @@ -301,8 +367,11 @@ public class OpenstackSwitchingManager implements OpenstackSwitchingService { |
| 301 | OpenstackSwitchingRulePopulator rulePopulator = | 367 | OpenstackSwitchingRulePopulator rulePopulator = |
| 302 | new OpenstackSwitchingRulePopulator(appId, flowObjectiveService, | 368 | new OpenstackSwitchingRulePopulator(appId, flowObjectiveService, |
| 303 | deviceService, restHandler, driverService); | 369 | deviceService, restHandler, driverService); |
| 370 | + | ||
| 304 | rulePopulator.populateSwitchingRules(doNotPushFlows, device, port); | 371 | rulePopulator.populateSwitchingRules(doNotPushFlows, device, port); |
| 305 | - updatePortMap(device.id(), port, restHandler.getNetworks(), rulePopulator.openstackPort(port)); | 372 | + updatePortMap(device.id(), port, restHandler.getNetworks(), restHandler.getSubnets(), |
| 373 | + rulePopulator.openstackPort(port)); | ||
| 374 | + | ||
| 306 | //In case portupdate event is driven by vm shutoff from openstack | 375 | //In case portupdate event is driven by vm shutoff from openstack |
| 307 | } else if (!port.isEnabled() && openstackPortInfoMap.containsKey(port.annotations().value(PORTNAME))) { | 376 | } else if (!port.isEnabled() && openstackPortInfoMap.containsKey(port.annotations().value(PORTNAME))) { |
| 308 | log.debug("Flowrules according to the port {} were removed", port.number().toString()); | 377 | log.debug("Flowrules according to the port {} were removed", port.number().toString()); |
| ... | @@ -327,6 +396,7 @@ public class OpenstackSwitchingManager implements OpenstackSwitchingService { | ... | @@ -327,6 +396,7 @@ public class OpenstackSwitchingManager implements OpenstackSwitchingService { |
| 327 | deviceService, restHandler, driverService); | 396 | deviceService, restHandler, driverService); |
| 328 | 397 | ||
| 329 | Collection<OpenstackNetwork> networks = restHandler.getNetworks(); | 398 | Collection<OpenstackNetwork> networks = restHandler.getNetworks(); |
| 399 | + Collection<OpenstackSubnet> subnets = restHandler.getSubnets(); | ||
| 330 | 400 | ||
| 331 | deviceService.getDevices().forEach(device -> { | 401 | deviceService.getDevices().forEach(device -> { |
| 332 | log.debug("device {} num of ports {} ", device.id(), | 402 | log.debug("device {} num of ports {} ", device.id(), |
| ... | @@ -339,7 +409,7 @@ public class OpenstackSwitchingManager implements OpenstackSwitchingService { | ... | @@ -339,7 +409,7 @@ public class OpenstackSwitchingManager implements OpenstackSwitchingService { |
| 339 | if (osPort != null && !osPort.deviceOwner().equals(DEVICE_OWNER_GATEWAY)) { | 409 | if (osPort != null && !osPort.deviceOwner().equals(DEVICE_OWNER_GATEWAY)) { |
| 340 | if (!doNotPushFlows) { | 410 | if (!doNotPushFlows) { |
| 341 | rulePopulator.populateSwitchingRules(doNotPushFlows, device, vmPort); | 411 | rulePopulator.populateSwitchingRules(doNotPushFlows, device, vmPort); |
| 342 | - updatePortMap(device.id(), vmPort, networks, osPort); | 412 | + updatePortMap(device.id(), vmPort, networks, subnets, osPort); |
| 343 | } | 413 | } |
| 344 | registerDhcpInfo(osPort); | 414 | registerDhcpInfo(osPort); |
| 345 | } else { | 415 | } else { |
| ... | @@ -352,16 +422,23 @@ public class OpenstackSwitchingManager implements OpenstackSwitchingService { | ... | @@ -352,16 +422,23 @@ public class OpenstackSwitchingManager implements OpenstackSwitchingService { |
| 352 | } | 422 | } |
| 353 | 423 | ||
| 354 | private void updatePortMap(DeviceId deviceId, Port port, Collection<OpenstackNetwork> networks, | 424 | private void updatePortMap(DeviceId deviceId, Port port, Collection<OpenstackNetwork> networks, |
| 355 | - OpenstackPort openstackPort) { | 425 | + Collection<OpenstackSubnet> subnets, OpenstackPort openstackPort) { |
| 356 | long vni = Long.parseLong(networks.stream() | 426 | long vni = Long.parseLong(networks.stream() |
| 357 | .filter(n -> n.id().equals(openstackPort.networkId())) | 427 | .filter(n -> n.id().equals(openstackPort.networkId())) |
| 358 | .findAny().orElse(null).segmentId()); | 428 | .findAny().orElse(null).segmentId()); |
| 359 | 429 | ||
| 430 | + OpenstackSubnet openstackSubnet = subnets.stream() | ||
| 431 | + .filter(n -> n.networkId().equals(openstackPort.networkId())) | ||
| 432 | + .findFirst().get(); | ||
| 433 | + | ||
| 434 | + Ip4Address gatewayIPAddress = Ip4Address.valueOf(openstackSubnet.gatewayIp()); | ||
| 435 | + | ||
| 360 | OpenstackPortInfo.Builder portBuilder = OpenstackPortInfo.builder() | 436 | OpenstackPortInfo.Builder portBuilder = OpenstackPortInfo.builder() |
| 361 | .setDeviceId(deviceId) | 437 | .setDeviceId(deviceId) |
| 362 | .setHostIp((Ip4Address) openstackPort.fixedIps().values().stream().findFirst().orElse(null)) | 438 | .setHostIp((Ip4Address) openstackPort.fixedIps().values().stream().findFirst().orElse(null)) |
| 363 | .setHostMac(openstackPort.macAddress()) | 439 | .setHostMac(openstackPort.macAddress()) |
| 364 | - .setVni(vni); | 440 | + .setVni(vni) |
| 441 | + .setGatewayIP(gatewayIPAddress); | ||
| 365 | 442 | ||
| 366 | openstackPortInfoMap.putIfAbsent(port.annotations().value(PORTNAME), | 443 | openstackPortInfoMap.putIfAbsent(port.annotations().value(PORTNAME), |
| 367 | portBuilder.build()); | 444 | portBuilder.build()); |
| ... | @@ -420,7 +497,6 @@ public class OpenstackSwitchingManager implements OpenstackSwitchingService { | ... | @@ -420,7 +497,6 @@ public class OpenstackSwitchingManager implements OpenstackSwitchingService { |
| 420 | 497 | ||
| 421 | @Override | 498 | @Override |
| 422 | public void process(PacketContext context) { | 499 | public void process(PacketContext context) { |
| 423 | - | ||
| 424 | if (context.isHandled()) { | 500 | if (context.isHandled()) { |
| 425 | return; | 501 | return; |
| 426 | } | 502 | } |
| ... | @@ -429,7 +505,7 @@ public class OpenstackSwitchingManager implements OpenstackSwitchingService { | ... | @@ -429,7 +505,7 @@ public class OpenstackSwitchingManager implements OpenstackSwitchingService { |
| 429 | Ethernet ethernet = pkt.parsed(); | 505 | Ethernet ethernet = pkt.parsed(); |
| 430 | 506 | ||
| 431 | if (ethernet != null && ethernet.getEtherType() == Ethernet.TYPE_ARP) { | 507 | if (ethernet != null && ethernet.getEtherType() == Ethernet.TYPE_ARP) { |
| 432 | - arpHandler.processPacketIn(pkt); | 508 | + arpHandler.processPacketIn(pkt, openstackPortInfoMap.values()); |
| 433 | } | 509 | } |
| 434 | } | 510 | } |
| 435 | } | 511 | } |
| ... | @@ -510,6 +586,7 @@ public class OpenstackSwitchingManager implements OpenstackSwitchingService { | ... | @@ -510,6 +586,7 @@ public class OpenstackSwitchingManager implements OpenstackSwitchingService { |
| 510 | return; | 586 | return; |
| 511 | } | 587 | } |
| 512 | doNotPushFlows = cfg.doNotPushFlows(); | 588 | doNotPushFlows = cfg.doNotPushFlows(); |
| 589 | + physicalRouterMac = cfg.physicalRouterMac(); | ||
| 513 | restHandler = new OpenstackRestHandler(cfg); | 590 | restHandler = new OpenstackRestHandler(cfg); |
| 514 | arpHandler = new OpenstackArpHandler(restHandler, packetService, hostService); | 591 | arpHandler = new OpenstackArpHandler(restHandler, packetService, hostService); |
| 515 | initializeFlowRules(); | 592 | initializeFlowRules(); | ... | ... |
| ... | @@ -42,6 +42,7 @@ import org.onosproject.net.flowobjective.FlowObjectiveService; | ... | @@ -42,6 +42,7 @@ import org.onosproject.net.flowobjective.FlowObjectiveService; |
| 42 | import org.onosproject.net.flowobjective.ForwardingObjective; | 42 | import org.onosproject.net.flowobjective.ForwardingObjective; |
| 43 | import org.onosproject.openstackswitching.OpenstackNetwork; | 43 | import org.onosproject.openstackswitching.OpenstackNetwork; |
| 44 | import org.onosproject.openstackswitching.OpenstackPort; | 44 | import org.onosproject.openstackswitching.OpenstackPort; |
| 45 | +import org.onosproject.openstackswitching.OpenstackPortInfo; | ||
| 45 | import org.slf4j.Logger; | 46 | import org.slf4j.Logger; |
| 46 | import org.slf4j.LoggerFactory; | 47 | import org.slf4j.LoggerFactory; |
| 47 | 48 | ... | ... |
| 1 | +/* | ||
| 2 | + * Copyright 2016 Open Networking Laboratory | ||
| 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.openstackswitching.web; | ||
| 17 | + | ||
| 18 | +import com.fasterxml.jackson.databind.ObjectMapper; | ||
| 19 | +import com.fasterxml.jackson.databind.node.ObjectNode; | ||
| 20 | +import org.onosproject.openstackrouting.OpenstackRouter; | ||
| 21 | +import org.onosproject.openstackrouting.OpenstackRouterInterface; | ||
| 22 | +import org.onosproject.openstackswitching.OpenstackSwitchingService; | ||
| 23 | +import org.onosproject.rest.AbstractWebResource; | ||
| 24 | +import org.slf4j.Logger; | ||
| 25 | +import org.slf4j.LoggerFactory; | ||
| 26 | + | ||
| 27 | +import javax.ws.rs.Consumes; | ||
| 28 | +import javax.ws.rs.DELETE; | ||
| 29 | +import javax.ws.rs.POST; | ||
| 30 | +import javax.ws.rs.PUT; | ||
| 31 | +import javax.ws.rs.Path; | ||
| 32 | +import javax.ws.rs.PathParam; | ||
| 33 | +import javax.ws.rs.Produces; | ||
| 34 | +import javax.ws.rs.core.MediaType; | ||
| 35 | +import javax.ws.rs.core.Response; | ||
| 36 | +import java.io.InputStream; | ||
| 37 | +import static com.google.common.base.Preconditions.checkNotNull; | ||
| 38 | + | ||
| 39 | +/** | ||
| 40 | + * Handles REST API call of Neturon L3 plugin. | ||
| 41 | + */ | ||
| 42 | + | ||
| 43 | +@Path("routers") | ||
| 44 | +public class OpensatckRouterWebResource extends AbstractWebResource { | ||
| 45 | + protected static final Logger log = LoggerFactory | ||
| 46 | + .getLogger(OpenstackNetworkWebResource.class); | ||
| 47 | + | ||
| 48 | + private static final OpenstackRouterInterfaceCodec ROUTER_INTERFACE_CODEC | ||
| 49 | + = new OpenstackRouterInterfaceCodec(); | ||
| 50 | + private static final OpenstackRouterCodec ROUTER_CODEC | ||
| 51 | + = new OpenstackRouterCodec(); | ||
| 52 | + | ||
| 53 | + @POST | ||
| 54 | + @Consumes(MediaType.APPLICATION_JSON) | ||
| 55 | + @Produces(MediaType.APPLICATION_JSON) | ||
| 56 | + public Response createRouter(InputStream input) { | ||
| 57 | + checkNotNull(input); | ||
| 58 | + try { | ||
| 59 | + ObjectMapper mapper = new ObjectMapper(); | ||
| 60 | + ObjectNode routerNode = (ObjectNode) mapper.readTree(input); | ||
| 61 | + | ||
| 62 | + OpenstackRouter openstackRouter | ||
| 63 | + = ROUTER_CODEC.decode(routerNode, this); | ||
| 64 | + | ||
| 65 | + OpenstackSwitchingService switchingService | ||
| 66 | + = getService(OpenstackSwitchingService.class); | ||
| 67 | + switchingService.createRouter(openstackRouter); | ||
| 68 | + | ||
| 69 | + log.debug("REST API CREATE router is called {}", input.toString()); | ||
| 70 | + return Response.status(Response.Status.OK).build(); | ||
| 71 | + } catch (Exception e) { | ||
| 72 | + log.error("Create Router failed because of exception {}", | ||
| 73 | + e.toString()); | ||
| 74 | + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(e.toString()) | ||
| 75 | + .build(); | ||
| 76 | + } | ||
| 77 | + } | ||
| 78 | + | ||
| 79 | + @PUT | ||
| 80 | + @Path("{id}") | ||
| 81 | + @Consumes(MediaType.APPLICATION_JSON) | ||
| 82 | + @Produces(MediaType.APPLICATION_JSON) | ||
| 83 | + public Response updateRouter(@PathParam("id") String id) { | ||
| 84 | + checkNotNull(id); | ||
| 85 | + try { | ||
| 86 | + OpenstackSwitchingService switchingService | ||
| 87 | + = getService(OpenstackSwitchingService.class); | ||
| 88 | + switchingService.updateRouter(id); | ||
| 89 | + | ||
| 90 | + log.debug("REST API UPDATE router is called from router {}", id); | ||
| 91 | + return Response.status(Response.Status.OK).build(); | ||
| 92 | + } catch (Exception e) { | ||
| 93 | + log.error("Updates Router failed because of exception {}", | ||
| 94 | + e.toString()); | ||
| 95 | + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(e.toString()) | ||
| 96 | + .build(); | ||
| 97 | + } | ||
| 98 | + } | ||
| 99 | + | ||
| 100 | + @PUT | ||
| 101 | + @Path("{id}/add_router_interface") | ||
| 102 | + @Consumes(MediaType.APPLICATION_JSON) | ||
| 103 | + @Produces(MediaType.APPLICATION_JSON) | ||
| 104 | + public Response addRouterInterface(InputStream input) { | ||
| 105 | + checkNotNull(input); | ||
| 106 | + try { | ||
| 107 | + ObjectMapper mapper = new ObjectMapper(); | ||
| 108 | + ObjectNode routerIfNode = (ObjectNode) mapper.readTree(input); | ||
| 109 | + | ||
| 110 | + OpenstackRouterInterface openstackRouterInterface | ||
| 111 | + = ROUTER_INTERFACE_CODEC.decode(routerIfNode, this); | ||
| 112 | + | ||
| 113 | + OpenstackSwitchingService switchingService | ||
| 114 | + = getService(OpenstackSwitchingService.class); | ||
| 115 | + switchingService.updateRouterInterface(openstackRouterInterface); | ||
| 116 | + | ||
| 117 | + log.debug("REST API AddRouterInterface is called from router {} portId: {}, subnetId: {}, tenantId: {}", | ||
| 118 | + openstackRouterInterface.id(), openstackRouterInterface.portId(), | ||
| 119 | + openstackRouterInterface.subnetId(), openstackRouterInterface.tenantId()); | ||
| 120 | + | ||
| 121 | + return Response.status(Response.Status.OK).build(); | ||
| 122 | + } catch (Exception e) { | ||
| 123 | + log.error("AddRouterInterface failed because of exception {}", | ||
| 124 | + e.toString()); | ||
| 125 | + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(e.toString()) | ||
| 126 | + .build(); | ||
| 127 | + } | ||
| 128 | + } | ||
| 129 | + | ||
| 130 | + @DELETE | ||
| 131 | + @Path("{id}") | ||
| 132 | + public Response deleteRouter(@PathParam("id") String id) { | ||
| 133 | + checkNotNull(id); | ||
| 134 | + OpenstackSwitchingService switchingService = | ||
| 135 | + getService(OpenstackSwitchingService.class); | ||
| 136 | + switchingService.deleteRouter(id); | ||
| 137 | + | ||
| 138 | + log.debug("REST API DELETE routers is called {}", id); | ||
| 139 | + return Response.status(Response.Status.OK).build(); | ||
| 140 | + } | ||
| 141 | + | ||
| 142 | + @PUT | ||
| 143 | + @Path("{id}/remove_router_interface") | ||
| 144 | + @Consumes(MediaType.APPLICATION_JSON) | ||
| 145 | + @Produces(MediaType.APPLICATION_JSON) | ||
| 146 | + public Response removeRouterInterface(@PathParam("id") String id, InputStream input) { | ||
| 147 | + checkNotNull(id); | ||
| 148 | + checkNotNull(input); | ||
| 149 | + try { | ||
| 150 | + ObjectMapper mapper = new ObjectMapper(); | ||
| 151 | + ObjectNode routerIfNode = (ObjectNode) mapper.readTree(input); | ||
| 152 | + | ||
| 153 | + OpenstackRouterInterface openstackRouterInterface | ||
| 154 | + = ROUTER_INTERFACE_CODEC.decode(routerIfNode, this); | ||
| 155 | + | ||
| 156 | + OpenstackSwitchingService switchingService | ||
| 157 | + = getService(OpenstackSwitchingService.class); | ||
| 158 | + switchingService.removeRouterInterface(openstackRouterInterface); | ||
| 159 | + | ||
| 160 | + log.debug("REST API RemoveRouterInterface is called from router {} portId: {}, subnetId: {}," + | ||
| 161 | + "tenantId: {}", openstackRouterInterface.id(), openstackRouterInterface.portId(), | ||
| 162 | + openstackRouterInterface.subnetId(), openstackRouterInterface.tenantId()); | ||
| 163 | + | ||
| 164 | + return Response.status(Response.Status.OK).build(); | ||
| 165 | + } catch (Exception e) { | ||
| 166 | + log.error("RemoveRouterInterface failed because of exception {}", | ||
| 167 | + e.toString()); | ||
| 168 | + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(e.toString()) | ||
| 169 | + .build(); | ||
| 170 | + } | ||
| 171 | + } | ||
| 172 | +} |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2015 Open Networking Laboratory | 2 | + * Copyright 2015-2016 Open Networking Laboratory |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. | ... | ... |
| 1 | +/* | ||
| 2 | + * Copyright 2016 Open Networking Laboratory | ||
| 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.openstackswitching.web; | ||
| 17 | + | ||
| 18 | +import com.fasterxml.jackson.databind.JsonNode; | ||
| 19 | +import com.fasterxml.jackson.databind.node.ArrayNode; | ||
| 20 | +import com.fasterxml.jackson.databind.node.ObjectNode; | ||
| 21 | +import com.google.common.collect.Maps; | ||
| 22 | +import org.onlab.packet.Ip4Address; | ||
| 23 | +import org.onosproject.codec.CodecContext; | ||
| 24 | +import org.onosproject.codec.JsonCodec; | ||
| 25 | +import org.onosproject.openstackrouting.OpenstackExternalGateway; | ||
| 26 | +import org.onosproject.openstackrouting.OpenstackRouter; | ||
| 27 | +import org.slf4j.Logger; | ||
| 28 | +import org.slf4j.LoggerFactory; | ||
| 29 | +import static com.google.common.base.Preconditions.checkNotNull; | ||
| 30 | + | ||
| 31 | +import java.util.Map; | ||
| 32 | +/** | ||
| 33 | + * Implementation of the OpenstackRouter Codec. | ||
| 34 | + */ | ||
| 35 | +public class OpenstackRouterCodec extends JsonCodec<OpenstackRouter> { | ||
| 36 | + protected static final Logger log = LoggerFactory | ||
| 37 | + .getLogger(OpenstackNetworkCodec.class); | ||
| 38 | + | ||
| 39 | + private static final String ROUTER = "router"; | ||
| 40 | + private static final String TENANT_ID = "tenant_id"; | ||
| 41 | + private static final String NETWORK_ID = "network_id"; | ||
| 42 | + private static final String ID = "id"; | ||
| 43 | + private static final String NAME = "name"; | ||
| 44 | + private static final String STATUS = "status"; | ||
| 45 | + private static final String ADMIN_STATE_UP = "admin_state_up"; | ||
| 46 | + private static final String EXTERNAL_GW_INFO = "external_gateway_info"; | ||
| 47 | + private static final String EXTERNAL_FIXED_IPS = "external_fixed_ips"; | ||
| 48 | + private static final String SUBNET_ID = "subnet_id"; | ||
| 49 | + private static final String IP_ADDRESS = "ip_address"; | ||
| 50 | + | ||
| 51 | + /** | ||
| 52 | + * Decodes the OpenstackRouter. | ||
| 53 | + * | ||
| 54 | + * @param json JSON to decode | ||
| 55 | + * @param context decoding context | ||
| 56 | + * @return OpenstackRouter | ||
| 57 | + */ | ||
| 58 | + @Override | ||
| 59 | + public OpenstackRouter decode(ObjectNode json, CodecContext context) { | ||
| 60 | + | ||
| 61 | + if (json == null || !json.isObject()) { | ||
| 62 | + return null; | ||
| 63 | + } | ||
| 64 | + JsonNode routerInfo = json.get(ROUTER); | ||
| 65 | + if (routerInfo == null) { | ||
| 66 | + routerInfo = json; | ||
| 67 | + } | ||
| 68 | + | ||
| 69 | + String tenantId = checkNotNull(routerInfo.path(TENANT_ID).asText()); | ||
| 70 | + String id = checkNotNull(routerInfo.path(ID).asText()); | ||
| 71 | + String name = checkNotNull(routerInfo.path(NAME).asText()); | ||
| 72 | + String status = checkNotNull(routerInfo.path(STATUS).asText()); | ||
| 73 | + String adminStateUp = checkNotNull(routerInfo.path(ADMIN_STATE_UP).asText()); | ||
| 74 | + | ||
| 75 | + OpenstackExternalGateway.Builder osExtBuiler = OpenstackExternalGateway.builder(); | ||
| 76 | + | ||
| 77 | + if (!routerInfo.path(EXTERNAL_GW_INFO).isMissingNode()) { | ||
| 78 | + String externalGatewayNetId = checkNotNull(routerInfo.path(EXTERNAL_GW_INFO).path(NETWORK_ID).asText()); | ||
| 79 | + Map<String, Ip4Address> fixedIpMap = Maps.newHashMap(); | ||
| 80 | + | ||
| 81 | + | ||
| 82 | + if (!routerInfo.path(EXTERNAL_GW_INFO).path(EXTERNAL_FIXED_IPS).isMissingNode()) { | ||
| 83 | + ArrayNode fixedIpList = (ArrayNode) routerInfo.path(EXTERNAL_GW_INFO).path(EXTERNAL_FIXED_IPS); | ||
| 84 | + | ||
| 85 | + for (JsonNode fixedIpInfo : fixedIpList) { | ||
| 86 | + String subnetId = checkNotNull(fixedIpInfo.path(SUBNET_ID).asText()); | ||
| 87 | + String ipAddressStr = checkNotNull(fixedIpInfo.path(IP_ADDRESS).asText()); | ||
| 88 | + if (!fixedIpInfo.path(IP_ADDRESS).isMissingNode() && ipAddressStr != null) { | ||
| 89 | + fixedIpMap.put(subnetId, Ip4Address.valueOf(ipAddressStr)); | ||
| 90 | + } | ||
| 91 | + } | ||
| 92 | + } | ||
| 93 | + | ||
| 94 | + osExtBuiler.networkId(externalGatewayNetId) | ||
| 95 | + .enablePnat(true) | ||
| 96 | + .externalFixedIps(fixedIpMap); | ||
| 97 | + } | ||
| 98 | + OpenstackRouter.Builder osBuilder = new OpenstackRouter.Builder() | ||
| 99 | + .tenantId(tenantId) | ||
| 100 | + .id(id) | ||
| 101 | + .name(name) | ||
| 102 | + .status(OpenstackRouter.RouterStatus.valueOf(status)) | ||
| 103 | + .adminStateUp(Boolean.valueOf(adminStateUp)) | ||
| 104 | + .gatewayExternalInfo(osExtBuiler.build()); | ||
| 105 | + | ||
| 106 | + return osBuilder.build(); | ||
| 107 | + } | ||
| 108 | +} |
| 1 | +/* | ||
| 2 | + * Copyright 2016 Open Networking Laboratory | ||
| 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.openstackswitching.web; | ||
| 17 | + | ||
| 18 | +import com.fasterxml.jackson.databind.JsonNode; | ||
| 19 | +import com.fasterxml.jackson.databind.node.ObjectNode; | ||
| 20 | +import org.onosproject.codec.CodecContext; | ||
| 21 | +import org.onosproject.codec.JsonCodec; | ||
| 22 | +import org.onosproject.openstackrouting.OpenstackRouterInterface; | ||
| 23 | +import org.slf4j.Logger; | ||
| 24 | +import org.slf4j.LoggerFactory; | ||
| 25 | +import static com.google.common.base.Preconditions.checkNotNull; | ||
| 26 | +/** | ||
| 27 | + * Implementation of the OpenstackRouterInterface Codec. | ||
| 28 | + */ | ||
| 29 | +public class OpenstackRouterInterfaceCodec extends JsonCodec<OpenstackRouterInterface> { | ||
| 30 | + protected static final Logger log = LoggerFactory | ||
| 31 | + .getLogger(OpenstackNetworkCodec.class); | ||
| 32 | + | ||
| 33 | + private static final String ID = "id"; | ||
| 34 | + private static final String TENANT_ID = "tenant_id"; | ||
| 35 | + private static final String SUBNET_ID = "subnet_id"; | ||
| 36 | + private static final String PORT_ID = "port_id"; | ||
| 37 | + | ||
| 38 | + /** | ||
| 39 | + * Decodes the OpenstackRouterInterface. | ||
| 40 | + * | ||
| 41 | + * @param json JSON to decode | ||
| 42 | + * @param context decoding context | ||
| 43 | + * @return OpenstackRouterInterface | ||
| 44 | + */ | ||
| 45 | + @Override | ||
| 46 | + public OpenstackRouterInterface decode(ObjectNode json, CodecContext context) { | ||
| 47 | + if (json == null || !json.isObject()) { | ||
| 48 | + return null; | ||
| 49 | + } | ||
| 50 | + JsonNode routerIfInfo = json; | ||
| 51 | + | ||
| 52 | + String id = checkNotNull(routerIfInfo.path(ID).asText()); | ||
| 53 | + String tenantId = checkNotNull(routerIfInfo.path(TENANT_ID).asText()); | ||
| 54 | + String subnetId = checkNotNull(routerIfInfo.path(SUBNET_ID).asText()); | ||
| 55 | + String portId = checkNotNull(routerIfInfo.path(PORT_ID).asText()); | ||
| 56 | + | ||
| 57 | + OpenstackRouterInterface.Builder osBuilder = new OpenstackRouterInterface.Builder() | ||
| 58 | + .id(id) | ||
| 59 | + .tenantId(tenantId) | ||
| 60 | + .subnetId(subnetId) | ||
| 61 | + .portId(portId); | ||
| 62 | + | ||
| 63 | + return osBuilder.build(); | ||
| 64 | + } | ||
| 65 | +} |
| ... | @@ -32,7 +32,8 @@ | ... | @@ -32,7 +32,8 @@ |
| 32 | <param-value> | 32 | <param-value> |
| 33 | org.onosproject.openstackswitching.web.OpenstackPortWebResource, | 33 | org.onosproject.openstackswitching.web.OpenstackPortWebResource, |
| 34 | org.onosproject.openstackswitching.web.OpenstackNetworkWebResource, | 34 | org.onosproject.openstackswitching.web.OpenstackNetworkWebResource, |
| 35 | - org.onosproject.openstackswitching.web.OpenstackSubnetWebResource | 35 | + org.onosproject.openstackswitching.web.OpenstackSubnetWebResource, |
| 36 | + org.onosproject.openstackswitching.web.OpensatckRouterWebResource | ||
| 36 | </param-value> | 37 | </param-value> |
| 37 | </init-param> | 38 | </init-param> |
| 38 | <load-on-startup>1</load-on-startup> | 39 | <load-on-startup>1</load-on-startup> | ... | ... |
| ... | @@ -37,6 +37,11 @@ | ... | @@ -37,6 +37,11 @@ |
| 37 | </modules> | 37 | </modules> |
| 38 | 38 | ||
| 39 | <dependencies> | 39 | <dependencies> |
| 40 | + <dependency> | ||
| 41 | + <groupId>org.onosproject</groupId> | ||
| 42 | + <artifactId>onos-app-openstackrouting</artifactId> | ||
| 43 | + <version>${project.version}</version> | ||
| 44 | + </dependency> | ||
| 40 | </dependencies> | 45 | </dependencies> |
| 41 | 46 | ||
| 42 | 47 | ... | ... |
-
Please register or login to post a comment