티스토리 뷰

작성 계기

작년 졸업 프로젝트에서, 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 문서에 잘 나와 있다.)

처음에는 DeviceStore의, device를 추가·갱신하는 createOrUpdateDevice 메서드에 꽂혀서 이걸 바로 호출하려 했었다. 그러나 곧바로 ProviderId 관련 문제에 부딪혔다.

  • OpenFlow device를 위한 provider의 ProviderId를 적용해서 임의의 device를 코어에 직접 추가했을 때, 이 device는 실제로는 OpenFlow 프로토콜로 연결된 device가 아니므로, 사용 가능하지 않은 device로 조회되었다.
  • 임의의(등록하지 않은) ProviderId를 적용하여 임의의 device를 코어에 추가했을 때, 이 device의 provider가 현재 등록되어 있지 않으므로(이 device를 관리할 provider가 없으므로), 임의로 추가한 device가 사용 가능하지 않은 device로 조회되었다.

나는 여기서 별도의 provider를 직접 새로 등록하여 사용해야 한다는 결론을 내렸다. 그리고 DeviceStorecreateOrUpdateDevice 메서드를 바로 호출하여 코어에 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

더 찾아보기

댓글
공지사항