blob: e9d2b2f30606b3f756067ead8c3a620f0ef5d83a [file] [log] [blame]
Jian Liecae4382018-06-28 17:41:12 +09001/*
2 * Copyright 2018-present Open Networking Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.onosproject.openstacknetworking.impl;
17
18import com.google.common.collect.ImmutableSet;
19import org.apache.felix.scr.annotations.Activate;
20import org.apache.felix.scr.annotations.Component;
21import org.apache.felix.scr.annotations.Deactivate;
22import org.apache.felix.scr.annotations.Reference;
23import org.apache.felix.scr.annotations.ReferenceCardinality;
24import org.apache.felix.scr.annotations.Service;
25import org.onlab.util.KryoNamespace;
26import org.onosproject.core.ApplicationId;
27import org.onosproject.core.CoreService;
28import org.onosproject.openstacknetworking.api.InstancePort;
29import org.onosproject.openstacknetworking.api.InstancePortEvent;
30import org.onosproject.openstacknetworking.api.InstancePortStore;
31import org.onosproject.openstacknetworking.api.InstancePortStoreDelegate;
32import org.onosproject.store.AbstractStore;
33import org.onosproject.store.serializers.KryoNamespaces;
34import org.onosproject.store.service.ConsistentMap;
35import org.onosproject.store.service.MapEvent;
36import org.onosproject.store.service.MapEventListener;
37import org.onosproject.store.service.Serializer;
38import org.onosproject.store.service.StorageService;
39import org.onosproject.store.service.Versioned;
40import org.slf4j.Logger;
41
42import java.util.Set;
43import java.util.concurrent.ExecutorService;
44
45import static com.google.common.base.Preconditions.checkArgument;
46import static java.util.concurrent.Executors.newSingleThreadExecutor;
47import static org.onlab.util.Tools.groupedThreads;
48import static org.onosproject.openstacknetworking.api.Constants.OPENSTACK_NETWORKING_APP_ID;
49import static org.onosproject.openstacknetworking.api.InstancePort.State.ACTIVE;
50import static org.onosproject.openstacknetworking.api.InstancePort.State.INACTIVE;
Jian Liec5c32b2018-07-13 14:28:58 +090051import static org.onosproject.openstacknetworking.api.InstancePort.State.MIGRATED;
Jian Liecae4382018-06-28 17:41:12 +090052import static org.onosproject.openstacknetworking.api.InstancePort.State.MIGRATING;
53import static org.onosproject.openstacknetworking.api.InstancePortEvent.Type.OPENSTACK_INSTANCE_MIGRATION_ENDED;
54import static org.onosproject.openstacknetworking.api.InstancePortEvent.Type.OPENSTACK_INSTANCE_MIGRATION_STARTED;
55import static org.onosproject.openstacknetworking.api.InstancePortEvent.Type.OPENSTACK_INSTANCE_PORT_DETECTED;
56import static org.onosproject.openstacknetworking.api.InstancePortEvent.Type.OPENSTACK_INSTANCE_PORT_UPDATED;
57import static org.onosproject.openstacknetworking.api.InstancePortEvent.Type.OPENSTACK_INSTANCE_PORT_VANISHED;
58import static org.onosproject.openstacknetworking.api.InstancePortEvent.Type.OPENSTACK_INSTANCE_RESTARTED;
59import static org.onosproject.openstacknetworking.api.InstancePortEvent.Type.OPENSTACK_INSTANCE_TERMINATED;
60import static org.slf4j.LoggerFactory.getLogger;
61
62/**
63 * Manages the inventory of openstack instance port using a {@code ConsistentMap}.
64 */
65@Service
66@Component(immediate = true)
67public class DistributedInstancePortStore
68 extends AbstractStore<InstancePortEvent, InstancePortStoreDelegate>
69 implements InstancePortStore {
70
71 protected final Logger log = getLogger(getClass());
72
73 private static final String ERR_NOT_FOUND = " does not exist";
74 private static final String ERR_DUPLICATE = " already exists";
75
76 private static final KryoNamespace SERIALIZER_INSTANCE_PORT = KryoNamespace.newBuilder()
77 .register(KryoNamespaces.API)
78 .register(InstancePort.class)
79 .register(DefaultInstancePort.class)
80 .register(InstancePort.State.class)
81 .build();
82
83 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
84 protected CoreService coreService;
85
86 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
87 protected StorageService storageService;
88
89 private final ExecutorService eventExecutor = newSingleThreadExecutor(
90 groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
91
92 private final MapEventListener<String, InstancePort>
93 instancePortMapListener = new InstancePortMapListener();
94
95 private ConsistentMap<String, InstancePort> instancePortStore;
96
97 @Activate
98 protected void activate() {
99 ApplicationId appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
100
101 instancePortStore = storageService.<String, InstancePort>consistentMapBuilder()
102 .withSerializer(Serializer.using(SERIALIZER_INSTANCE_PORT))
103 .withName("openstack-instanceport-store")
104 .withApplicationId(appId)
105 .build();
106 instancePortStore.addListener(instancePortMapListener);
107
108 log.info("Started");
109 }
110
111 @Deactivate
112 protected void deactivate() {
113 instancePortStore.removeListener(instancePortMapListener);
114 eventExecutor.shutdown();
115
116 log.info("Stopped");
117 }
118
119 @Override
120 public void createInstancePort(InstancePort port) {
121 instancePortStore.compute(port.portId(), (id, existing) -> {
122 final String error = port.portId() + ERR_DUPLICATE;
123 checkArgument(existing == null, error);
124 return port;
125 });
126 }
127
128 @Override
129 public void updateInstancePort(InstancePort port) {
130 instancePortStore.compute(port.portId(), (id, existing) -> {
131 final String error = port.portId() + ERR_NOT_FOUND;
132 checkArgument(existing != null, error);
133 return port;
134 });
135 }
136
137 @Override
138 public InstancePort removeInstancePort(String portId) {
139 Versioned<InstancePort> port = instancePortStore.remove(portId);
140 return port == null ? null : port.value();
141 }
142
143 @Override
144 public InstancePort instancePort(String portId) {
145 return instancePortStore.asJavaMap().get(portId);
146 }
147
148 @Override
149 public Set<InstancePort> instancePorts() {
150 return ImmutableSet.copyOf(instancePortStore.asJavaMap().values());
151 }
152
153 @Override
154 public void clear() {
155 instancePortStore.clear();
156 }
157
158 private class InstancePortMapListener implements MapEventListener<String, InstancePort> {
159
160 @Override
161 public void event(MapEvent<String, InstancePort> event) {
162 switch (event.type()) {
163 case INSERT:
164 log.debug("Instance port created");
165 eventExecutor.execute(() ->
166 notifyDelegate(new InstancePortEvent(
167 OPENSTACK_INSTANCE_PORT_DETECTED,
168 event.newValue().value()))
169 );
170 break;
171 case UPDATE:
172 log.debug("Instance port updated");
Jian Liec5c32b2018-07-13 14:28:58 +0900173 eventExecutor.execute(() -> processInstancePortUpdate(event));
Jian Liecae4382018-06-28 17:41:12 +0900174 break;
175 case REMOVE:
176 log.debug("Instance port removed");
177 eventExecutor.execute(() ->
178 notifyDelegate(new InstancePortEvent(
179 OPENSTACK_INSTANCE_PORT_VANISHED,
180 event.oldValue().value()))
181 );
182 break;
183 default:
184 log.error("Unsupported instance port event type");
185 break;
186 }
187 }
188
189 private void processInstancePortUpdate(MapEvent<String, InstancePort> event) {
190 InstancePort.State oldState = event.oldValue().value().state();
191 InstancePort.State newState = event.newValue().value().state();
192
Jian Liee8214a2018-07-21 20:07:28 +0900193 if ((oldState == ACTIVE || oldState == INACTIVE) && newState == MIGRATING) {
Jian Liecae4382018-06-28 17:41:12 +0900194 notifyDelegate(new InstancePortEvent(
195 OPENSTACK_INSTANCE_MIGRATION_STARTED,
196 event.newValue().value()));
Jian Liec5c32b2018-07-13 14:28:58 +0900197 return;
Jian Liecae4382018-06-28 17:41:12 +0900198 }
199
Jian Liec5c32b2018-07-13 14:28:58 +0900200 if (oldState == MIGRATING && newState == MIGRATED) {
Jian Liecae4382018-06-28 17:41:12 +0900201 notifyDelegate(new InstancePortEvent(
202 OPENSTACK_INSTANCE_MIGRATION_ENDED,
203 event.newValue().value()));
Jian Liec5c32b2018-07-13 14:28:58 +0900204 updateInstancePort(event.newValue().value().updateState(ACTIVE));
205 return;
Jian Liecae4382018-06-28 17:41:12 +0900206 }
207
208 if (oldState == ACTIVE && newState == INACTIVE) {
209 notifyDelegate(new InstancePortEvent(
210 OPENSTACK_INSTANCE_TERMINATED,
211 event.newValue().value()));
Jian Liec5c32b2018-07-13 14:28:58 +0900212 return;
Jian Liecae4382018-06-28 17:41:12 +0900213 }
214
215 if (oldState == INACTIVE && newState == ACTIVE) {
216 notifyDelegate(new InstancePortEvent(
217 OPENSTACK_INSTANCE_RESTARTED,
218 event.newValue().value()));
Jian Liec5c32b2018-07-13 14:28:58 +0900219 return;
Jian Liecae4382018-06-28 17:41:12 +0900220 }
Jian Liec5c32b2018-07-13 14:28:58 +0900221
222 // this should be auto-transition
223 if (oldState == MIGRATED && newState == ACTIVE) {
224 return;
225 }
226
227 notifyDelegate(new InstancePortEvent(
228 OPENSTACK_INSTANCE_PORT_UPDATED,
229 event.newValue().value()));
Jian Liecae4382018-06-28 17:41:12 +0900230 }
231 }
232}