ODL(OpenDayLight) 기본 튜토리얼 #7_3 – clustering 설정

앞선 예제에서 사용했던 hello 프로젝트를 clustering을 이용하도록 구현해보자.

목표는 global-rpc,routed-rpc 구현과 datastore 공유이다.

hello 프로젝트에 아무런 설정을 하지 않고

1
ansible-playbook -i hosts playbook_hello.yaml

명령어를 이용하여 3개의 docker instance로 배포를한뒤 karaf를 실행시키고 hello-world-write RPC 를 이용하여 datastore에 데이터를 저장하면 3개의 노드 전부 datastore가 복제된다.

DataChangeListener의 경우에는 3개 Node에서 전부 실행 되는게 아니라 특정 Node 한곳에서만 실행이 되는듯 해보이는데 이건 좀더 분석을 해봐야 할것 같다.


이제 coretutorials에 있는 singletonsimple 을 직접 hello 프로젝트에 구현을 해보자

일단 기존에 생성된 hello 프로젝트는 ODL lithium 버전으로 만들어져서 이걸 boron 버전으로 변경해줘야 한다. 그런데 lithium 버전은 blueprint를 사용하지 않기 때문에 수동으로 impl 모듈에 blueprint.xml 파일을 넣어주고 기존에 만들어진 module yang 관련 파일은 삭제를 해줘야 한다.

1
2
3
impl/src/main 하위에 resources 를 만들고 폴더명이 org/opendaylight/blueprint 인 package 폴더를 만들고 impl-blueprint.xml 파일을 만들어 준다.

폴더를 생성할때 꼭 확인을 해야 할것이 폴더명이 org.opendaylight.blueprint 으로 되지 않았는지 확인이 필요하다. 폴더구조의 형태로 만들어져야 한다.

impl-blueprint.xml 파일 내용은 아래와 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<?xml version="1.0" encoding="UTF-8"?>

<!-- vi: set et smarttab sw=4 tabstop=4: -->

<!--
Copyright © 2016 Copyright(c) Chun, Inc. and others. All rights reserved.

This program and the accompanying materials are made available under the
terms of the Eclipse Public License v1.0 which accompanies this distribution,
and is available at http://www.eclipse.org/legal/epl-v10.html
-->
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
xmlns:odl="http://opendaylight.org/xmlns/blueprint/v1.0.0"
odl:use-default-for-reference-types="true">

<reference id="dataBroker"
interface="org.opendaylight.controller.md.sal.binding.api.DataBroker"
odl:type="default" />

<reference id="rpcRegistry"
interface="org.opendaylight.controller.sal.binding.api.RpcProviderRegistry"/>

<reference id="notificationService"
interface="org.opendaylight.controller.md.sal.binding.api.NotificationPublishService"/>

<reference id="clusterSingletonService"
interface="org.opendaylight.mdsal.singleton.common.api.ClusterSingletonServiceProvider"/>

<bean id="provider"
class="org.opendaylight.hello.impl.HelloProvider"
init-method="init" destroy-method="close">
<argument ref="dataBroker" />
<argument ref="rpcRegistry" />
<argument ref="notificationService" />
<argument ref="clusterSingletonService" />
</bean>

</blueprint>

impl-blueprint.xml 파일에서 수정해야 할 부분은 bean 설정에서 class 명을 자신이 생성한 provider class와 일치해주면 된다.

blueprint 설정이 끝났으면 기존에 lithium 버전에서 사용했던 파일들을 삭제해줘야 정상적으로 blueprint 사용이 가능하다.

삭제할 파일 및 폴

  • test 폴더
  • main/yang 폴더
  • config 폴더
  • java/org/opendaylight 하위에 있는 yang 풀더

다음으로 각 모듈의 pom 파일을 수정해야 한다.

  • impl 모듈의 pom.xml : version -> 0.5.2-Boron-SR2 으로 변경
  • root pom.xml : version -> 1.7.2-Boron-SR2 으로 변경
  • api 모듈의 pom.xml : version -> 0.9.2-Boron-SR2 으로 변경
  • artifacts 모듈의 pom.xml : version -> 1.8.0-SNAPSHOT 으로 변경
  • karaf 모듈의 pom.xml : version -> 1.7.2-Boron-SR2 으로 변경
  • feature 모듈의 pom.xml : version -> 1.7.2-Boron-SR2 으로 변경 , properties tag 안의 내용을 아래와 같이 변경
1
2
3
4
5
6
7
8
<properties>
<mdsal.model.version>0.10.0-SNAPSHOT</mdsal.model.version>
<mdsal.version>1.5.0-SNAPSHOT</mdsal.version>
<restconf.version>1.5.0-SNAPSHOT</restconf.version>
<yangtools.version>1.1.0-SNAPSHOT</yangtools.version>
<dlux.version>0.5.0-SNAPSHOT</dlux.version>
<configfile.directory>etc/opendaylight/karaf</configfile.directory>
</properties>

다음엔 HelloProvider.java 파일을 boron 버전에 맞게 수정해줘야 한다.

아래 코드는 소스의 일부분인데 implements 부분이 변경 되었고 생성자로 blueprint 에서 넘겨주는 값을 받도록 설정 하였다.

그리고 ClusterSingletonService 인터페이스의 메소드인 instantiateServiceInstance 메소드에 global rpc 서비스를 등록해주어 singleton cluster 사용을 하도록 수정해준다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
public class HelloProvider implements ClusterSingletonService {

private static final Logger LOG = LoggerFactory.getLogger(HelloProvider.class);
private DataBroker db;
private RpcRegistration<HelloService> helloService;
private InstanceIdentifier<HelloWorld> path;
private ListenerRegistration<DataChangeListener> data_change_listener;

private final HostInformation hostInfo = new HostInformation();

//boron version
private final RpcProviderRegistry rpcProviderRegistry;
private final NotificationPublishService notificationPublishService;
private final ClusterSingletonServiceProvider clusterSingletonServiceProvider;
private ClusterSingletonServiceRegistration cssRegistration;
private RpcRegistration<GlobalRpcService> globalRpcServiceReg;

private static final ServiceGroupIdentifier IDENT = ServiceGroupIdentifier.create("Brm");

public HelloProvider(final DataBroker dataBroker,
final RpcProviderRegistry rpcProviderRegistry,
final NotificationPublishService notificationPublishService,
final ClusterSingletonServiceProvider clusterSingletonServiceProvider) {
this.db = dataBroker;
this.rpcProviderRegistry = rpcProviderRegistry;
this.notificationPublishService = notificationPublishService;
this.clusterSingletonServiceProvider = clusterSingletonServiceProvider;
}

public void init() {
LOG.info("HelloProvider Session Initiated");
HelloWorldImpl helloWorldImpl=new HelloWorldImpl();
this.path=helloWorldImpl.HELLO_IID;
helloWorldImpl.setDb(this.db);

//registerDataChangeListener(접근하고자 하는 데이터 트리 종류(config or operational), 데이터 트리 PATH(InstanceIdentifier로 정의),실행될 객체, 변화감지 범위);
this.data_change_listener=this.db.registerDataChangeListener(LogicalDatastoreType.CONFIGURATION,path,helloWorldImpl,DataChangeScope.SUBTREE);

//boron
cssRegistration = clusterSingletonServiceProvider.registerClusterSingletonService(this);
this.helloService = rpcProviderRegistry.addRpcImplementation(HelloService.class,helloWorldImpl);
}

public void close() throws Exception {
LOG.info("HelloProvider Closed");

if (cssRegistration != null) {
try {
cssRegistration.close();
} catch (final Exception e) {
LOG.warn("Unexpected close exception", e);
}
cssRegistration = null;
}

if (helloService != null) {
helloService.close();
helloService = null;
}
}

@Override
public void instantiateServiceInstance() {
LOG.info("We take Leadership");
Preconditions.checkState(globalRpcServiceReg == null,
"Unexpected state: we have active GlobalRpcServiceRegistration");

// Create a new instance of the Global RPC Service and register it with the RPC registry
globalRpcServiceReg = rpcProviderRegistry.addRpcImplementation(GlobalRpcService.class,
new GlobalRpcServiceImpl(hostInfo));
}

@Override
public ListenableFuture<Void> closeServiceInstance() {
LOG.info("We lost Leadership");

// Unregister the Global RPC instance
if (globalRpcServiceReg != null) {
globalRpcServiceReg.close();
globalRpcServiceReg = null;
}
return Futures.immediateFuture(null);
}

@Override
public ServiceGroupIdentifier getIdentifier() {
return IDENT;
}
}

global-rpc를 사용하기 위해서는 features.xml 에 odl-mdsal-clustering 를 등록해줘야 한다.

1
2
3
4
5
6
7
<feature name='odl-hello' version='${project.version}' description='OpenDaylight :: hello'>
<feature version='${mdsal.version}'>odl-mdsal-broker</feature>
<feature version='${project.version}'>odl-hello-api</feature>
<feature version='${mdsal.version}'>odl-mdsal-clustering</feature>
<bundle>mvn:org.opendaylight.hello/hello-impl/{{VERSION}}</bundle>
<configfile finalname="${configfile.directory}/hello.xml">mvn:org.opendaylight.hello/hello-impl/{{VERSION}}/xml/config</configfile>
</feature>

마지막으로 singletonsimple 프로젝트의 yang 파일중에 commons.yang , global-rpc.yang 파일을 hello 프로젝트의 api 폴더에 있는 yang 폴더로 옮겨주고

GlobalRpcServiceImpl.java , HostInformation.java 파일도 impl/src/main/java 하위에 HelloProvider.java 파일이 있는곳에 옮겨주면 singletonsimple 에서 구현된 global-rpc 를 사용 할 수 있다.

공유하기