Bug 6714 - Use singleton service in clustered netconf topology
[netconf.git] / netconf / netconf-topology-singleton / src / main / java / org / opendaylight / netconf / topology / singleton / impl / MasterSalFacade.java
1 /*
2  * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8
9 package org.opendaylight.netconf.topology.singleton.impl;
10
11 import akka.actor.ActorRef;
12 import akka.actor.ActorSystem;
13 import akka.cluster.Cluster;
14 import akka.dispatch.OnComplete;
15 import akka.pattern.Patterns;
16 import com.google.common.base.Optional;
17 import com.google.common.base.Preconditions;
18 import java.util.List;
19 import java.util.stream.Collectors;
20 import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
21 import org.opendaylight.controller.md.sal.dom.api.DOMNotification;
22 import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
23 import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
24 import org.opendaylight.controller.sal.core.api.Broker;
25 import org.opendaylight.netconf.sal.connect.api.RemoteDeviceHandler;
26 import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfDeviceCapabilities;
27 import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfSessionPreferences;
28 import org.opendaylight.netconf.sal.connect.netconf.sal.NetconfDeviceNotificationService;
29 import org.opendaylight.netconf.sal.connect.netconf.sal.NetconfDeviceSalProvider;
30 import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId;
31 import org.opendaylight.netconf.topology.singleton.api.NetconfDOMTransaction;
32 import org.opendaylight.netconf.topology.singleton.impl.tx.NetconfMasterDOMTransaction;
33 import org.opendaylight.netconf.topology.singleton.impl.utils.NetconfTopologyUtils;
34 import org.opendaylight.netconf.topology.singleton.messages.CreateInitialMasterActorData;
35 import org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil;
36 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
37 import org.opendaylight.yangtools.yang.model.repo.api.RevisionSourceIdentifier;
38 import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
39 import org.slf4j.Logger;
40 import org.slf4j.LoggerFactory;
41 import scala.concurrent.Future;
42
43 class MasterSalFacade implements AutoCloseable, RemoteDeviceHandler<NetconfSessionPreferences> {
44
45     private static final Logger LOG = LoggerFactory.getLogger(MasterSalFacade.class);
46
47     private final RemoteDeviceId id;
48
49     private SchemaContext remoteSchemaContext = null;
50     private NetconfSessionPreferences netconfSessionPreferences = null;
51     private DOMRpcService deviceRpc = null;
52     private final NetconfDeviceSalProvider salProvider;
53
54     private final ActorRef masterActorRef;
55     private final ActorSystem actorSystem;
56     private DOMDataBroker deviceDataBroker = null;
57
58     MasterSalFacade(final RemoteDeviceId id,
59                            final Broker domBroker,
60                            final BindingAwareBroker bindingBroker,
61                            final ActorSystem actorSystem,
62                            final ActorRef masterActorRef) {
63         this.id = id;
64         this.salProvider = new NetconfDeviceSalProvider(id);
65         this.actorSystem = actorSystem;
66         this.masterActorRef = masterActorRef;
67
68         registerToSal(domBroker, bindingBroker);
69     }
70
71     private void registerToSal(final Broker domRegistryDependency, final BindingAwareBroker bindingBroker) {
72         // TODO: remove use of provider, there is possible directly create mount instance and
73         // TODO: NetconfDeviceTopologyAdapter in constructor = less complexity
74
75         domRegistryDependency.registerProvider(salProvider);
76         bindingBroker.registerProvider(salProvider);
77     }
78
79     @Override
80     public void onDeviceConnected(final SchemaContext remoteSchemaContext,
81                                   final NetconfSessionPreferences netconfSessionPreferences,
82                                   final DOMRpcService deviceRpc) {
83         this.remoteSchemaContext = remoteSchemaContext;
84         this.netconfSessionPreferences = netconfSessionPreferences;
85         this.deviceRpc = deviceRpc;
86
87         registerMasterMountPoint();
88
89         sendInitialDataToActor().onComplete(new OnComplete<Object>() {
90             @Override
91             public void onComplete(final Throwable failure, final Object success) throws Throwable {
92                 if (failure == null) {
93                     updateDeviceData();
94                     return;
95                 }
96                 throw failure;
97             }
98         }, actorSystem.dispatcher());
99
100     }
101
102     @Override
103     public void onDeviceDisconnected() {
104         salProvider.getTopologyDatastoreAdapter().updateDeviceData(false, new NetconfDeviceCapabilities());
105         unregisterMasterMountPoint();
106     }
107
108     @Override
109     public void onDeviceFailed(final Throwable throwable) {
110         salProvider.getTopologyDatastoreAdapter().setDeviceAsFailed(throwable);
111         unregisterMasterMountPoint();
112     }
113
114     @Override
115     public void onNotification(final DOMNotification domNotification) {
116         salProvider.getMountInstance().publish(domNotification);
117     }
118
119     @Override
120     public void close() {
121         unregisterMasterMountPoint();
122         closeGracefully(salProvider);
123     }
124
125     private void registerMasterMountPoint() {
126         Preconditions.checkNotNull(id);
127         Preconditions.checkNotNull(remoteSchemaContext,
128                 "Device has no remote schema context yet. Probably not fully connected.");
129         Preconditions.checkNotNull(netconfSessionPreferences,
130                 "Device has no capabilities yet. Probably not fully connected.");
131
132         final NetconfDeviceNotificationService notificationService = new NetconfDeviceNotificationService();
133
134         LOG.info("Creating master data broker for device {}", id);
135
136         final NetconfDOMTransaction masterDOMTransactions =
137                 new NetconfMasterDOMTransaction(id, remoteSchemaContext, deviceRpc, netconfSessionPreferences);
138         deviceDataBroker =
139                 new NetconfDOMDataBroker(actorSystem, id, masterDOMTransactions);
140         salProvider.getMountInstance()
141                 .onTopologyDeviceConnected(remoteSchemaContext, deviceDataBroker, deviceRpc, notificationService);
142     }
143
144     private Future<Object> sendInitialDataToActor() {
145         final List<SourceIdentifier> sourceIdentifiers =
146                 remoteSchemaContext.getAllModuleIdentifiers().stream().map(mi ->
147                         RevisionSourceIdentifier.create(mi.getName(),
148                             (SimpleDateFormatUtil.DEFAULT_DATE_REV == mi.getRevision() ? Optional.<String>absent() :
149                                     Optional.of(SimpleDateFormatUtil.getRevisionFormat().format(mi.getRevision())))))
150                         .collect(Collectors.toList());
151
152         // send initial data to master actor and create actor for providing it
153         return Patterns.ask(masterActorRef, new CreateInitialMasterActorData(deviceDataBroker, sourceIdentifiers),
154                 NetconfTopologyUtils.TIMEOUT);
155     }
156
157     private void updateDeviceData() {
158         Cluster cluster = Cluster.get(actorSystem);
159         salProvider.getTopologyDatastoreAdapter().updateClusteredDeviceData(true, cluster.selfAddress().toString(),
160                 netconfSessionPreferences.getNetconfDeviceCapabilities());
161     }
162
163     private void unregisterMasterMountPoint() {
164         salProvider.getMountInstance().onTopologyDeviceDisconnected();
165     }
166
167     private void closeGracefully(final AutoCloseable resource) {
168         if (resource != null) {
169             try {
170                 resource.close();
171             } catch (final Exception e) {
172                 LOG.warn("{}: Ignoring exception while closing {}", id, resource, e);
173             }
174         }
175     }
176
177 }