f034f372d28214bb2d826dc39750b0364d1b9e72
[onosfw.git] /
1 /*
2  * Copyright 2015 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.segmentrouting.config;
17
18 import java.io.File;
19 import java.io.IOException;
20 import java.util.ArrayList;
21 import java.util.HashMap;
22 import java.util.HashSet;
23 import java.util.List;
24 import java.util.Map;
25 import java.util.Set;
26 import java.util.concurrent.ConcurrentHashMap;
27 import java.util.concurrent.ConcurrentMap;
28
29 import org.onosproject.net.DeviceId;
30 import org.onosproject.net.Link;
31 import org.onosproject.segmentrouting.config.NetworkConfig.LinkConfig;
32 import org.onosproject.segmentrouting.config.NetworkConfig.SwitchConfig;
33 import org.slf4j.Logger;
34 import org.slf4j.LoggerFactory;
35
36 import com.fasterxml.jackson.core.JsonParseException;
37 import com.fasterxml.jackson.databind.JsonMappingException;
38 import com.fasterxml.jackson.databind.ObjectMapper;
39
40 /**
41  * NetworkConfigManager manages all network configuration for switches, links
42  * and any other state that needs to be configured for correct network
43  * operation.
44  *
45  */
46 public class NetworkConfigManager implements NetworkConfigService {
47     protected static final Logger log = LoggerFactory
48             .getLogger(NetworkConfigManager.class);
49     private static final String CONFIG_DIR = "../config";
50     private static final String DEFAULT_CONFIG_FILE = "segmentrouting.conf";
51     private final String configFileName = DEFAULT_CONFIG_FILE;
52     /**
53      * JSON Config file needs to use one of the following types for defining the
54      * kind of switch or link it wishes to configure.
55      */
56     public static final String SEGMENT_ROUTER = "Router_SR";
57
58     public static final String PKT_LINK = "pktLink";
59
60     NetworkConfig networkConfig;
61     private ConcurrentMap<DeviceId, SwitchConfig> configuredSwitches;
62     private ConcurrentMap<Link, LinkConfig> configuredLinks;
63     private Map<String, DeviceId> nameToDpid;
64
65     @Override
66     public SwitchConfigStatus checkSwitchConfig(DeviceId dpid) {
67         SwitchConfig swc = configuredSwitches.get(dpid);
68         if (networkConfig.getRestrictSwitches()) {
69             // default deny behavior
70             if (swc == null) {
71                 // switch is not configured - we deny this switch
72                 return new SwitchConfigStatus(NetworkConfigState.DENY, null,
73                         "Switch not configured, in network denying switches by default.");
74             }
75             if (swc.isAllowed()) {
76                 // switch is allowed in config, return configured attributes
77                 return new SwitchConfigStatus(NetworkConfigState.ACCEPT_ADD, swc);
78             } else {
79                 // switch has been configured off (administratively down)
80                 return new SwitchConfigStatus(NetworkConfigState.DENY, null,
81                         "Switch configured down (allowed=false).");
82             }
83         } else {
84             // default allow behavior
85             if (swc == null) {
86                 // no config to add
87                 return new SwitchConfigStatus(NetworkConfigState.ACCEPT, null);
88             }
89             if (swc.isAllowed()) {
90                 // switch is allowed in config, return configured attributes
91                 return new SwitchConfigStatus(NetworkConfigState.ACCEPT_ADD, swc);
92             } else {
93                 // switch has been configured off (administratively down)
94                 return new SwitchConfigStatus(NetworkConfigState.DENY, null,
95                         "Switch configured down (allowed=false).");
96             }
97         }
98
99     }
100
101     @Override
102     public LinkConfigStatus checkLinkConfig(Link linkTuple) {
103         LinkConfig lkc = getConfiguredLink(linkTuple);
104         // links are always disallowed if any one of the nodes that make up the
105         // link are disallowed
106         DeviceId linkNode1 = linkTuple.src().deviceId();
107         SwitchConfigStatus scs1 = checkSwitchConfig(linkNode1);
108         if (scs1.getConfigState() == NetworkConfigState.DENY) {
109             return new LinkConfigStatus(NetworkConfigState.DENY, null,
110                     "Link-node: " + linkNode1 + " denied by config: " + scs1.getMsg());
111         }
112         DeviceId linkNode2 = linkTuple.dst().deviceId();
113         SwitchConfigStatus scs2 = checkSwitchConfig(linkNode2);
114         if (scs2.getConfigState() == NetworkConfigState.DENY) {
115             return new LinkConfigStatus(NetworkConfigState.DENY, null,
116                     "Link-node: " + linkNode2 + " denied by config: " + scs2.getMsg());
117         }
118         if (networkConfig.getRestrictLinks()) {
119             // default deny behavior
120             if (lkc == null) {
121                 // link is not configured - we deny this link
122                 return new LinkConfigStatus(NetworkConfigState.DENY, null,
123                         "Link not configured, in network denying links by default.");
124             }
125             if (lkc.isAllowed()) {
126                 // link is allowed in config, return configured attributes
127                 return new LinkConfigStatus(NetworkConfigState.ACCEPT_ADD, lkc);
128             } else {
129                 // link has been configured off (administratively down)
130                 return new LinkConfigStatus(NetworkConfigState.DENY, null,
131                         "Link configured down (allowed=false).");
132             }
133         } else {
134             // default allow behavior
135             if (lkc == null) {
136                 // no config to add
137                 return new LinkConfigStatus(NetworkConfigState.ACCEPT, null);
138             }
139             if (lkc.isAllowed()) {
140                 // link is allowed in config, return configured attributes
141                 return new LinkConfigStatus(NetworkConfigState.ACCEPT_ADD, lkc);
142             } else {
143                 // link has been configured off (administratively down)
144                 return new LinkConfigStatus(NetworkConfigState.DENY, null,
145                         "Link configured down (allowed=false).");
146             }
147         }
148
149     }
150
151     @Override
152     public List<SwitchConfig> getConfiguredAllowedSwitches() {
153         List<SwitchConfig> allowed = new ArrayList<>();
154         for (SwitchConfig swc : configuredSwitches.values()) {
155             if (swc.isAllowed()) {
156                 allowed.add(swc);
157             }
158         }
159         return allowed;
160     }
161
162     @Override
163     public List<LinkConfig> getConfiguredAllowedLinks() {
164         List<LinkConfig> allowed = new ArrayList<>();
165         for (LinkConfig lkc : configuredLinks.values()) {
166             if (lkc.isAllowed()) {
167                 allowed.add(lkc);
168             }
169         }
170         return allowed;
171     }
172
173     @Override
174     public DeviceId getDpidForName(String name) {
175         if (nameToDpid.get(name) != null) {
176             return nameToDpid.get(name);
177         }
178         return null;
179     }
180
181     // **************
182     // Private methods
183     // **************
184
185     private void loadNetworkConfig() {
186         File configFile = new File(CONFIG_DIR, configFileName);
187         ObjectMapper mapper = new ObjectMapper();
188         networkConfig = new NetworkConfig();
189
190         try {
191             networkConfig = mapper.readValue(configFile,
192                                              NetworkConfig.class);
193         } catch (JsonParseException e) {
194             String err = String.format("JsonParseException while loading network "
195                     + "config from file: %s: %s", configFileName,
196                     e.getMessage());
197             throw new NetworkConfigException.ErrorConfig(err);
198         } catch (JsonMappingException e) {
199             String err = String.format(
200                     "JsonMappingException while loading network config "
201                             + "from file: %s: %s",
202                             configFileName,
203                             e.getMessage());
204             throw new NetworkConfigException.ErrorConfig(err);
205         } catch (IOException e) {
206             String err = String.format("IOException while loading network config "
207                     + "from file: %s %s", configFileName, e.getMessage());
208             throw new NetworkConfigException.ErrorConfig(err);
209         }
210
211         log.info("Network config specifies: {} switches and {} links",
212                 (networkConfig.getRestrictSwitches())
213                         ? networkConfig.getSwitchConfig().size() : "default allow",
214                         (networkConfig.getRestrictLinks())
215                         ? networkConfig.getLinkConfig().size() : "default allow");
216     }
217
218     private void parseNetworkConfig() {
219         List<SwitchConfig> swConfList = networkConfig.getSwitchConfig();
220         List<LinkConfig> lkConfList = networkConfig.getLinkConfig();
221         validateSwitchConfig(swConfList);
222         createTypeSpecificSwitchConfig(swConfList);
223         validateLinkConfig(lkConfList);
224         createTypeSpecificLinkConfig(lkConfList);
225         // TODO validate reachability matrix 'names' for configured dpids
226     }
227
228     private void createTypeSpecificSwitchConfig(List<SwitchConfig> swConfList) {
229         for (SwitchConfig swc : swConfList) {
230             nameToDpid.put(swc.getName(), swc.getDpid());
231             String swtype = swc.getType();
232             switch (swtype) {
233             case SEGMENT_ROUTER:
234                 SwitchConfig sr = new SegmentRouterConfig(swc);
235                 configuredSwitches.put(sr.getDpid(), sr);
236                 break;
237             default:
238                 throw new NetworkConfigException.UnknownSwitchType(swtype,
239                         swc.getName());
240             }
241         }
242     }
243
244     private void createTypeSpecificLinkConfig(List<LinkConfig> lkConfList) {
245         for (LinkConfig lkc : lkConfList) {
246             String lktype = lkc.getType();
247             switch (lktype) {
248             case PKT_LINK:
249                 PktLinkConfig pk = new PktLinkConfig(lkc);
250                 for (Link lt : pk.getLinkTupleList()) {
251                     configuredLinks.put(lt, pk);
252                 }
253                 break;
254             default:
255                 throw new NetworkConfigException.UnknownLinkType(lktype,
256                         lkc.getNodeDpid1(), lkc.getNodeDpid2());
257             }
258         }
259     }
260
261     private void validateSwitchConfig(List<SwitchConfig> swConfList) {
262         Set<DeviceId> swDpids = new HashSet<>();
263         Set<String> swNames = new HashSet<>();
264         for (SwitchConfig swc : swConfList) {
265             if (swc.getNodeDpid() == null || swc.getDpid() == null) {
266                 throw new NetworkConfigException.DpidNotSpecified(swc.getName());
267             }
268             // ensure both String and DeviceId values of dpid are set
269             if (!swc.getDpid().equals(DeviceId.deviceId(swc.getNodeDpid()))) {
270                 throw new NetworkConfigException.SwitchDpidNotConverted(
271                         swc.getName());
272             }
273             if (swc.getName() == null) {
274                 throw new NetworkConfigException.NameNotSpecified(swc.getDpid());
275             }
276             if (swc.getType() == null) {
277                 throw new NetworkConfigException.SwitchTypeNotSpecified(
278                         swc.getDpid());
279             }
280             if (!swDpids.add(swc.getDpid())) {
281                 throw new NetworkConfigException.DuplicateDpid(swc.getDpid());
282             }
283             if (!swNames.add(swc.getName())) {
284                 throw new NetworkConfigException.DuplicateName(swc.getName());
285             }
286             // TODO Add more validations
287         }
288     }
289
290     private void validateLinkConfig(List<LinkConfig> lkConfList) {
291         for (LinkConfig lkc : lkConfList) {
292             if (lkc.getNodeDpid1() == null || lkc.getNodeDpid2() == null) {
293                 throw new NetworkConfigException.LinkDpidNotSpecified(
294                         lkc.getNodeDpid1(), lkc.getNodeDpid2());
295             }
296             // ensure both String and Long values are set
297             if (!lkc.getDpid1().equals(DeviceId.deviceId(lkc.getNodeDpid1())) ||
298                     !lkc.getDpid2().equals(DeviceId.deviceId(lkc.getNodeDpid2()))) {
299                 throw new NetworkConfigException.LinkDpidNotConverted(
300                         lkc.getNodeDpid1(), lkc.getNodeDpid2());
301             }
302             if (lkc.getType() == null) {
303                 throw new NetworkConfigException.LinkTypeNotSpecified(
304                         lkc.getNodeDpid1(), lkc.getNodeDpid2());
305             }
306             if (configuredSwitches.get(lkc.getDpid1()) == null) {
307                 throw new NetworkConfigException.LinkForUnknownSwitchConfig(
308                         lkc.getNodeDpid1());
309             }
310             if (configuredSwitches.get(lkc.getDpid2()) == null) {
311                 throw new NetworkConfigException.LinkForUnknownSwitchConfig(
312                         lkc.getNodeDpid2());
313             }
314             // TODO add more validations
315         }
316
317     }
318
319     private LinkConfig getConfiguredLink(Link linkTuple) {
320         LinkConfig lkc = null;
321         // first try the unidirectional link with the ports assigned
322         lkc = configuredLinks.get(linkTuple);
323         return lkc;
324     }
325
326
327     /**
328      * Initializes the network configuration manager module by
329      * loading and parsing the network configuration file.
330      */
331     public void init() {
332         loadNetworkConfig();
333         configuredSwitches = new ConcurrentHashMap<>();
334         configuredLinks = new ConcurrentHashMap<>();
335         nameToDpid = new HashMap<>();
336         parseNetworkConfig();
337     }
338 }