Carolina Fernandez | ad89343 | 2016-07-18 11:11:34 +0200 | [diff] [blame^] | 1 | /* |
| 2 | * Copyright 2016-present 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 | |
| 17 | package org.onosproject.sdxl2; |
| 18 | |
| 19 | import com.google.common.collect.Iterables; |
| 20 | import org.apache.commons.lang.NotImplementedException; |
| 21 | import org.onlab.packet.VlanId; |
| 22 | import org.onosproject.core.ApplicationId; |
| 23 | import org.onosproject.net.flow.DefaultTrafficSelector; |
| 24 | import org.onosproject.net.flow.DefaultTrafficTreatment; |
| 25 | import org.onosproject.net.flow.TrafficSelector; |
| 26 | import org.onosproject.net.flow.TrafficTreatment; |
| 27 | import org.onosproject.net.intent.Constraint; |
| 28 | import org.onosproject.net.intent.Intent; |
| 29 | import org.onosproject.net.intent.IntentService; |
| 30 | import org.onosproject.net.intent.Key; |
| 31 | import org.slf4j.Logger; |
| 32 | import org.slf4j.LoggerFactory; |
| 33 | |
| 34 | import java.util.ArrayList; |
| 35 | import java.util.Collection; |
| 36 | import java.util.List; |
| 37 | import java.util.Objects; |
| 38 | import java.util.Optional; |
| 39 | import java.util.Set; |
| 40 | |
| 41 | import static java.lang.String.format; |
| 42 | |
| 43 | |
| 44 | /** |
| 45 | * Manages Virtual Circuits. |
| 46 | * Base class extended by different types of VCs. |
| 47 | */ |
| 48 | public class SdxL2VCManager implements SdxL2VCService { |
| 49 | |
| 50 | private static Logger log = LoggerFactory.getLogger(SdxL2VCManager.class); |
| 51 | |
| 52 | protected static final String MATCH_FORMAT = "%s-%s"; |
| 53 | protected static final String NAME_FORMAT = "%s:%s-%s"; |
| 54 | protected static final String SDXL2_CPS_FORMAT = "%s~%s"; |
| 55 | protected static final String KEY_FORMAT = "%s,%s"; |
| 56 | |
| 57 | protected ApplicationId appId; |
| 58 | protected SdxL2Store sdxL2Store; |
| 59 | protected IntentService intentService; |
| 60 | |
| 61 | private static String errorIntentsForward = "Unable to create forward Intents"; |
| 62 | private static String errorIntentsReverse = "Unable to create reverse Intents"; |
| 63 | protected static String errorCreateIntents = "Unable to create Intents for %s-%s"; |
| 64 | |
| 65 | |
| 66 | /** |
| 67 | * Creates an SDX-L2 VC Manager. |
| 68 | * |
| 69 | * @param sdxl2id application ID |
| 70 | * @param store reference to the SDX-L2 store |
| 71 | * @param intentService reference to the Intent service |
| 72 | */ |
| 73 | public SdxL2VCManager(ApplicationId sdxl2id, |
| 74 | SdxL2Store store, |
| 75 | IntentService intentService) { |
| 76 | |
| 77 | this.appId = sdxl2id; |
| 78 | this.sdxL2Store = store; |
| 79 | this.intentService = intentService; |
| 80 | } |
| 81 | |
| 82 | @Override |
| 83 | public void addVC(String sdxl2, SdxL2ConnectionPoint sdxl2cplhs, SdxL2ConnectionPoint sdxl2cprhs) { |
| 84 | try { |
| 85 | this.sdxL2Store.addVC(sdxl2, sdxl2cplhs, sdxl2cprhs); |
| 86 | |
| 87 | Collection<Intent> intentsFW = buildIntents(sdxl2, sdxl2cplhs, sdxl2cprhs); |
| 88 | Collection<Intent> intentsRV = buildIntents(sdxl2, sdxl2cprhs, sdxl2cplhs); |
| 89 | |
| 90 | if (intentsFW == null) { |
| 91 | System.err.println("\u001B[0;31mError executing command: " |
| 92 | + errorIntentsForward + "\u001B[0;49m"); |
| 93 | return; |
| 94 | } |
| 95 | if (intentsRV == null) { |
| 96 | System.err.println("\u001B[0;31mError executing command: " |
| 97 | + errorIntentsReverse + "\u001B[0;49m"); |
| 98 | return; |
| 99 | } |
| 100 | |
| 101 | List<Intent> intents = new ArrayList<Intent>(); |
| 102 | intents.addAll(intentsFW); |
| 103 | intents.addAll(intentsRV); |
| 104 | intents.forEach(intent -> { |
| 105 | intentService.submit(intent); |
| 106 | }); |
| 107 | } catch (SdxL2Exception e) { |
| 108 | log.error(e.getMessage()); |
| 109 | } |
| 110 | } |
| 111 | |
| 112 | @Override |
| 113 | public void removeVC(SdxL2ConnectionPoint sdxl2cplhs, SdxL2ConnectionPoint sdxl2cprhs) { |
| 114 | try { |
| 115 | this.sdxL2Store.removeVC(sdxl2cplhs, sdxl2cprhs); |
| 116 | Iterables.filter(intentService.getIntents(), intent -> |
| 117 | (matches(sdxl2cplhs, sdxl2cprhs, intent) || |
| 118 | (matches(sdxl2cprhs, sdxl2cplhs, intent)))) |
| 119 | .forEach(intentService::withdraw); |
| 120 | } catch (SdxL2Exception e) { |
| 121 | log.error(e.getMessage()); |
| 122 | } |
| 123 | } |
| 124 | |
| 125 | @Override |
| 126 | public void removeVC(SdxL2ConnectionPoint cp) { |
| 127 | try { |
| 128 | this.sdxL2Store.removeVC(cp); |
| 129 | Iterables.filter(intentService.getIntents(), intent -> (matches(cp, intent))) |
| 130 | .forEach(intentService::withdraw); |
| 131 | } catch (SdxL2Exception e) { |
| 132 | log.error(e.getMessage()); |
| 133 | } |
| 134 | } |
| 135 | |
| 136 | /** |
| 137 | * Creates a set of Intents for the ingress and egress SDX-L2 Connection Point. |
| 138 | * |
| 139 | * @param sdxl2 name of SDX-L2 |
| 140 | * @param ingress the ingress point with the relative traffic attributes |
| 141 | * @param egress the egress point with the relative traffic attributes |
| 142 | * @return a set of intent or a null set; |
| 143 | */ |
| 144 | public Collection<Intent> buildIntents(String sdxl2, SdxL2ConnectionPoint ingress, |
| 145 | SdxL2ConnectionPoint egress) { |
| 146 | throw new NotImplementedException("buildIntents not implemented"); |
| 147 | } |
| 148 | |
| 149 | /** |
| 150 | * Matches an intent given two SDX-L2 connection points. |
| 151 | * |
| 152 | * @param sdxl2cplhs left hand side of the virtual circuit |
| 153 | * @param sdxl2cprhs right hand side of the virtual circuit |
| 154 | * @param intent intent to match |
| 155 | * @return result of the match |
| 156 | */ |
| 157 | protected boolean matches(SdxL2ConnectionPoint sdxl2cplhs, SdxL2ConnectionPoint sdxl2cprhs, Intent intent) { |
| 158 | if (!Objects.equals(appId, intent.appId())) { |
| 159 | // different app ids |
| 160 | return false; |
| 161 | } |
| 162 | |
| 163 | String key = intent.key().toString(); |
| 164 | String[] fields = key.split(":"); |
| 165 | String cps = format(MATCH_FORMAT, sdxl2cplhs.name(), sdxl2cprhs.name()); |
| 166 | |
| 167 | return fields.length == 2 && fields[1].contains(cps); |
| 168 | } |
| 169 | |
| 170 | /** |
| 171 | * Matches an intent given an SDX-L2 Connection Point. |
| 172 | * |
| 173 | * @param cp hand side of a Virtual Circuit |
| 174 | * @param intent intent to match |
| 175 | * @return result of the match |
| 176 | */ |
| 177 | protected boolean matches(SdxL2ConnectionPoint cp, Intent intent) { |
| 178 | if (!Objects.equals(appId, intent.appId())) { |
| 179 | // different app ids |
| 180 | return false; |
| 181 | } |
| 182 | |
| 183 | String key = intent.key().toString(); |
| 184 | String[] fields = key.split(":"); |
| 185 | |
| 186 | if (fields.length != 2) { |
| 187 | return false; |
| 188 | } |
| 189 | String[] cps = fields[1].split(","); |
| 190 | |
| 191 | if (cps.length != 2) { |
| 192 | return false; |
| 193 | } |
| 194 | String[] hss = cps[0].split("-"); |
| 195 | |
| 196 | return hss.length == 2 && (hss[0].equals(cp.name()) || hss[1].equals(cp.name())); |
| 197 | } |
| 198 | |
| 199 | @Override |
| 200 | public void removeVCs(String sdxl2) { |
| 201 | this.sdxL2Store.removeVCs(sdxl2); |
| 202 | Iterables.filter(intentService.getIntents(), intent -> (matches(sdxl2, intent))) |
| 203 | .forEach(intentService::withdraw); |
| 204 | |
| 205 | } |
| 206 | |
| 207 | /** |
| 208 | * Matches an intent given an SDX-L2 Connection Point. |
| 209 | * |
| 210 | * @param sdxl2 name of SDX-L2 |
| 211 | * @param intent intent to match |
| 212 | * @return result of the match |
| 213 | */ |
| 214 | protected boolean matches(String sdxl2, Intent intent) { |
| 215 | if (!Objects.equals(appId, intent.appId())) { |
| 216 | // different app ids |
| 217 | return false; |
| 218 | } |
| 219 | |
| 220 | String key = intent.key().toString(); |
| 221 | String[] fields = key.split(":"); |
| 222 | |
| 223 | return fields.length == 2 && fields[0].equals(sdxl2); |
| 224 | } |
| 225 | |
| 226 | @Override |
| 227 | public Set<String> getVCs(Optional<String> sdxl2) { |
| 228 | return this.sdxL2Store.getVCs(sdxl2); |
| 229 | } |
| 230 | |
| 231 | @Override |
| 232 | public String getVC(SdxL2ConnectionPoint sdxl2cplhs, SdxL2ConnectionPoint sdxl2cprhs) { |
| 233 | try { |
| 234 | return this.sdxL2Store.getVC(sdxl2cplhs, sdxl2cprhs); |
| 235 | } catch (SdxL2Exception e) { |
| 236 | log.error(e.getMessage()); |
| 237 | } |
| 238 | return null; |
| 239 | } |
| 240 | |
| 241 | /** |
| 242 | * Returns Intent key from SDX-L2 and two SDX-L2 Connection Points. |
| 243 | * |
| 244 | * @param sdxl2 name of SDX-L2 |
| 245 | * @param cpone sdxl2 connection point one |
| 246 | * @param cptwo sdxl2 connection point two |
| 247 | * @param index digit used to help identify Intent |
| 248 | * @return canonical intent string key |
| 249 | */ |
| 250 | protected Key generateIntentKey(String sdxl2, SdxL2ConnectionPoint cpone, |
| 251 | SdxL2ConnectionPoint cptwo, String index) { |
| 252 | String cps = format(NAME_FORMAT, sdxl2, cpone.name(), cptwo.name()); |
| 253 | String key = format(KEY_FORMAT, cps, index); |
| 254 | return Key.of(key, appId); |
| 255 | } |
| 256 | |
| 257 | /** |
| 258 | * Returns the traffic treatment, used in the definition of the intents. |
| 259 | * |
| 260 | * @param setVlan VLAN to set |
| 261 | * @param pushVlan VLAN to push |
| 262 | * @param popVlan boolean to indicate whether a popVlan action is |
| 263 | * performed (true) or not (false) |
| 264 | * @return TrafficTreatment object |
| 265 | */ |
| 266 | protected TrafficTreatment buildTreatment(VlanId setVlan, |
| 267 | VlanId pushVlan, |
| 268 | boolean popVlan) { |
| 269 | TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder(); |
| 270 | if (setVlan != null) { |
| 271 | treatmentBuilder.setVlanId(setVlan); |
| 272 | } |
| 273 | if (pushVlan != null) { |
| 274 | treatmentBuilder.pushVlan(); |
| 275 | treatmentBuilder.setVlanId(pushVlan); |
| 276 | } |
| 277 | if (popVlan) { |
| 278 | treatmentBuilder.popVlan(); |
| 279 | } |
| 280 | return treatmentBuilder.build(); |
| 281 | } |
| 282 | |
| 283 | /** |
| 284 | * Returns the traffic selector, used in the definition of the intents. |
| 285 | * |
| 286 | * @param ethertype name of the Ethernet type used (e.g. of SDX-L2 |
| 287 | * @param ingresstag VLAN id used at the ingress |
| 288 | * @return TrafficSelector object |
| 289 | */ |
| 290 | protected TrafficSelector buildSelector(Short ethertype, VlanId ingresstag) { |
| 291 | TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder(); |
| 292 | if (ethertype != null) { |
| 293 | selectorBuilder.matchEthType(ethertype); |
| 294 | } |
| 295 | if (ingresstag != null) { |
| 296 | selectorBuilder.matchVlanId(ingresstag); |
| 297 | } |
| 298 | return selectorBuilder.build(); |
| 299 | } |
| 300 | |
| 301 | /** |
| 302 | * Returns constraints depending on the encapsulation used on the VC. |
| 303 | * |
| 304 | * @return list of constraints to be used in the intents |
| 305 | */ |
| 306 | protected List<Constraint> buildConstraints() { |
| 307 | throw new NotImplementedException("buildConstraints not implemented"); |
| 308 | } |
| 309 | } |