티스토리 뷰
작성 계기
작년 졸업 프로젝트에서, REST를 이용한 ONOS to ONOS orchestration을 구현해야 했었다. 이걸 위해서는 child ONOS에 REST로 요청하여 받은 device, link, host 정보를 parent ONOS의 코어에 반영해야 했었다.
일단 link와 host를 추가하기 전에, device가 어떻게 추가되는지부터 알아야 했다. 그래서 device가 추가되는 과정에서 찍히는 로그를 직접 따라가서, 코드와 함께 분석해 보았다. 그 내용은 다음과 같다. (사실 이 과정은 ONOS Wiki에 그림과 함께 잘 나와 있다. 전반적으로는 System Components 문서의 Subsystem Structure 문단에 잘 나와 있고, OpenFlow southbound 관점에서의 더 자세한 내용은 Device Subsystem 문서에 잘 나와 있다.)
OpenFlowDeviceProvider
가 새로 연결된 OpenFlow device 정보를 바탕으로ProviderService
를 통해DeviceManager
에게 device가 추가되었다고 전달한다.DeviceManager
는 전달받은 device 정보를DeviceStore
에 기록(추가 또는 갱신)하고 그 이벤트를 받아 전달(post)한다.
처음에는 DeviceStore
의, device를 추가·갱신하는 createOrUpdateDevice
메서드에 꽂혀서 이걸 바로 호출하려 했었다. 그러나 곧바로 ProviderId
관련 문제에 부딪혔다.
- OpenFlow device를 위한 provider의
ProviderId
를 적용해서 임의의 device를 코어에 직접 추가했을 때, 이 device는 실제로는 OpenFlow 프로토콜로 연결된 device가 아니므로, 사용 가능하지 않은 device로 조회되었다. - 임의의(등록하지 않은)
ProviderId
를 적용하여 임의의 device를 코어에 추가했을 때, 이 device의 provider가 현재 등록되어 있지 않으므로(이 device를 관리할 provider가 없으므로), 임의로 추가한 device가 사용 가능하지 않은 device로 조회되었다.
나는 여기서 별도의 provider를 직접 새로 등록하여 사용해야 한다는 결론을 내렸다. 그리고 DeviceStore
의 createOrUpdateDevice
메서드를 바로 호출하여 코어에 device를 추가하면 이벤트 발생 관련 문제가 생길 것 같아서, DeviceProviderService
를 통하여 코어에 device를 추가하기로 했다.
결국 OpenFlowDeviceProvider
와 DHCP 애플리케이션의 DhcpManager
를 참고하여, 이들과 비슷한 방법으로 DeviceProviderRegistry
로 provider를 등록하여 DeviceProviderService
를 얻고, 임의의 device를 코어에 추가하는 데 성공했다. 이어서 link와 host를 추가하는 데에도 성공했다.
나는 이 방법을 잊어버려서 이 고생을 다시 하고 싶지는 않았기 때문에 곧바로 매뉴얼을 작성해 두었다. 그리고 그걸 방치하여 썩혀 두기는 아까워서 이 글을 통해 공유하고자 한다.
이 글에서는 템플릿 애플리케이션을 변경하여 ONOS subsystem structure 중 provider component를 만드는 방법을 다룬다.
ONOS 템플릿 애플리케이션 생성
이 부분은 onos-create-app으로 새 ONOS 애플리케이션 만들기와 비슷하게 진행한다.
먼저 onos-create-app
으로 템플릿 애플리케이션을 생성한다. 예제에서는 ONOS_POM_VERSION
을 "2.2.1-b2"
로 맞춰 놓고 진행했다.
$ onos-create-app
...
Confirm properties configuration:
groupId: org.example
artifactId: myprovider
version: 1.0-SNAPSHOT
package: org.example.myprovider
...
pom.xml
에 모듈의 정보를 작성한다.
템플릿 애플리케이션 구조
이 예제는 다음과 같은 매우 단순한 구조에서 진행된다.
myprovider
├── pom.xml
└── src
└── main
└── java
└── org
└── example
└── myprovider
└── AppComponent.java
// src/main/java/org/example/myprovider/AppComponent.java
package org.example.myprovider;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Deactivate;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.component.annotations.ReferenceCardinality;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Component(immediate = true)
public class AppComponent {
private final Logger log = LoggerFactory.getLogger(getClass());
@Activate
protected void activate() {
log.info("Started");
}
@Deactivate
protected void deactivate() {
log.info("Stopped");
}
}
인터페이스 상속 및 구현
Provider class를 AppComponent
class의 inner class로 작성한다. 이 provider는 나중에 하나만 생성하여 AppComponent
class 내부에서만 사용할 것이다.
Provider class는 AbstractProvider
class를 상속받고, 구현하려는 Provider
interface를 구현한다. 상속받거나 구현해야 하는 메서드는 provider에 따라 다르다. 이 부분은 IDE의 기능을 활용하거나, AbstractProvider
class와 각 Provider
interface를 참고하여 작성한다. 이 예제에서는 InternalDeviceProvider
class가 AbstractProvider
class를 상속받고 DeviceProvider
interface를 구현한다.
// src/main/java/org/example/myprovider/AppComponent.java
// ...
import org.onosproject.net.DeviceId;
import org.onosproject.net.MastershipRole;
import org.onosproject.net.PortNumber;
import org.onosproject.net.device.DeviceProvider;
import org.onosproject.net.provider.AbstractProvider;
import org.onosproject.net.provider.ProviderId;
// ...
public class AppComponent {
// ...
private class InternalDeviceProvider extends AbstractProvider implements DeviceProvider {
protected InternalDeviceProvider(ProviderId id) {
super(id);
}
@Override
public void triggerProbe(DeviceId deviceId) {
}
@Override
public void roleChanged(DeviceId deviceId, MastershipRole newRole) {
}
@Override
public boolean isReachable(DeviceId deviceId) {
return true;
}
@Override
public void changePortState(DeviceId deviceId, PortNumber portNumber, boolean enable) {
}
}
}
Provider 초기화, 등록, 등록 해제
방금 만든 provider를 초기화한다.
// src/main/java/org/example/myprovider/AppComponent.java
// ...
public class AppComponent {
private final Logger log = LoggerFactory.getLogger(getClass());
private final String SCHEME = "myscheme";
private final String APP_NAME = "org.example.myprovider";
private final ProviderId PROVIDER_ID = new ProviderId(SCHEME, APP_NAME);
private final InternalDeviceProvider deviceProvider = new InternalDeviceProvider(PROVIDER_ID);
// ...
}
애플리케이션 활성화 시 provider를 등록하여, 추후 코어와 상호작용하는 데 사용할 ProviderService
를 받아오게 한다. 애플리케이션 비활성화 시 provider를 등록 해제하고 ProviderService
를 더 이상 사용하지 않게 한다.
// src/main/java/org/example/myprovider/AppComponent.java
// ...
import org.onosproject.net.device.DeviceProviderRegistry;
import org.onosproject.net.device.DeviceProviderService;
// ...
public class AppComponent {
// ...
private DeviceProviderService providerService;
@Reference(cardinality = ReferenceCardinality.MANDATORY)
private DeviceProviderRegistry providerRegistry;
@Activate
protected void activate() {
providerService = providerRegistry.register(deviceProvider);
log.info("Started");
}
@Deactivate
protected void deactivate() {
providerRegistry.unregister(deviceProvider);
providerService = null;
log.info("Stopped");
}
// ...
}
간단한 Provider 활용 예시: 코어에 Device 추가하기
앞서 작성한 애플리케이션을 활성화할 때 얻는 DeviceProviderService
를 다음과 같이 활용하면 ONOS 코어에 device를 추가할 수 있다.
// src/main/java/org/example/myprovider/AppComponent.java
// ...
import org.onlab.packet.ChassisId;
import org.onosproject.net.*;
import org.onosproject.net.device.*;
// ...
public class AppComponent {
// ...
public void addDevice(DeviceId deviceId) {
ChassisId chassisId = new ChassisId(deviceId.uri().getSchemeSpecificPart());
SparseAnnotations annotations = DefaultAnnotations.builder().build(); // empty annotations
DeviceDescription description = new DefaultDeviceDescription(deviceId.uri(), Device.Type.SWITCH,
"", "", "", "", chassisId, annotations);
log.info("Notifying the core that a device {} has connected.", deviceId);
providerService.deviceConnected(deviceId, description);
}
// ...
}
여기서 작성한 addDevice
메서드가 호출되면 디바이스가 하나 생길 것이다. 다음과 같이 activate
메서드에서 addDevice
메서드를 호출하도록 작성하고 빌드한 뒤, ONOS에 설치하고 활성화해 보았다.
// src/main/java/org/example/myprovider/AppComponent.java
// ...
public class AppComponent {
// ...
@Activate
protected void activate() {
providerService = providerRegistry.register(deviceProvider);
log.info("Started");
addDevice(DeviceId.deviceId("myscheme:0000000000000001"));
}
// ...
}
username@root > app activate org.example.myprovider
Activated org.example.myprovider
2020-01-23T03:16:24,057 | INFO | features-3-thread-1 | AppComponent | 209 - org.example.myprovider - 1.0.0.SNAPSHOT | Started
2020-01-23T03:16:24,057 | INFO | features-3-thread-1 | AppComponent | 209 - org.example.myprovider - 1.0.0.SNAPSHOT | Notifying the core a device myscheme:0000000000000001 has connected
2020-01-23T03:16:24,062 | INFO | features-3-thread-1 | DeviceManager | 190 - org.onosproject.onos-core-net - 2.3.0.rc2 | Local role is MASTER for myscheme:0000000000000001
2020-01-23T03:16:24,066 | INFO | features-3-thread-1 | DeviceManager | 190 - org.onosproject.onos-core-net - 2.3.0.rc2 | Device myscheme:0000000000000001 connected
2020-01-23T03:16:24,160 | INFO | onos-topo-build-2 | TopologyManager | 190 - org.onosproject.onos-core-net - 2.3.0.rc2 | Topology DefaultTopology{time=710307545430, creationTime=1579716984153, computeCost=86364, clusters=1, devices=1, links=0} changed
ONOS CLI에서 devices
명령어로 device 목록을 띄워 보면 애플리케이션에서 추가한 device 하나가 보인다.
username@root > devices
id=myscheme:0000000000000001, available=true, local-status=connected 5s ago, role=MASTER, type=SWITCH, mfr=, hw=, sw=, serial=, chassis=1, driver=onosfw
더 찾아보기
- ONOS Wiki
'2019 학부 졸업 프로젝트' 카테고리의 다른 글
Docker로 구동하는 ONOS에서 Inter-Domain Link LLDP 문제 해결하기 (0) | 2020.01.28 |
---|---|
onos-create-app으로 새 ONOS 애플리케이션 만들기 (0) | 2020.01.22 |
501 HTTPS Required 에러 없이 ONOS 빌드하기 (1) | 2020.01.20 |
Ubuntu에서 ONOS 빌드하고 실행하기 (2) | 2020.01.14 |
2019년 졸업 프로젝트(ONOS REST Orchestration) 진행 과정 (1) | 2020.01.09 |