Здравствуйте. Данная тема очень слабо раскрыта в Сети поскольку представляет интерес лишь в узких кругах. Чтобы немного восполнить этот пробел, данное место мне видится наиболее подходящим.

Интерфейс CORBA AlarmIRP присутствует во всех системах управления производителей оборудования с которыми мне пришлось столкнуться, поскольку предписан стандартом 3GPP 3G TS 32.106-2. Рассмотрим на примере OSS-RC Ericsson, в чьей документации процесс хоть как-то описан. Для NetAct Nokia и M-2000 Huawei код будет примерно такой же, с отличиями в нюансах реализации одного стандарта. Я постараюсь максимально понятно описать процесс создания приложения для чтения списка аварийных сообщений, но, поскольку, ранее я никогда не писал ничего на Java и не работал с CORBA некоторые детали позволю себе оставить за рамками данной статьи.

Всё приложении можно разбить на 3 части:

  1. получение IOR интерфейса
  2. создание объекта ссылающегося на интерфейс
  3. вызов методов интерфейса

Итак, согласно документации IOR хранится в двух местах: в файле
/var/opt/ericsson/blkcm/data/bulkcm.nameservice
и на web-сервере
http://«masterhost ip»:80/ior/ExternalNameService.ior
Воспользуемся первым способом:

private String readIOR() {
        String mastersvc = "/var/opt/ericsson/blkcm/data/bulkcm.nameservice";
        File f = new File(mastersvc);
        BufferedReader br;
        String iorContents = null;
		try {
			br = new BufferedReader(new FileReader(f));
	        	iorContents = br.readLine();
	        	br.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
        return iorContents;
}

Если быть точным, то метод выше нам вернёт не IOR, а ссылку на NameService (терминология CORBA), которую мы могли бы получить из IOR-файла на web-сервере. Если проще: результат мы будем использовать для инициализации подключения.

Второй этап инициализация объекта подключения ORB:

public void createAlarmObj(){
	org.omg.CORBA.Object rootObj = null;
	NamingContextExt rootNameCon = null;
	Properties props = new Properties();
	props.put("org.omg.CORBA.ORBInitRef", "NameService=" + readIOR());
	org.omg.CORBA.ORB orb = ORB.init(new String[0], props);
	// Resolve the CORBA Naming Service 
	try {
		rootObj = orb.resolve_initial_references("NameService");
		rootNameCon = NamingContextExtHelper.narrow(rootObj);
		String s = "com/ericsson/nms/fm_cirpagent/AlarmIRP";
		//Locate Alarm IRP
		rootObj = rootNameCon.resolve(rootNameCon.to_name(s));
		_alarmIrp = com.ericsson.irp.AlarmIRPSystem._AlarmIRPOperationsHelper.narrow(rootObj);
	} catch (InvalidName | NotFound | CannotProceed | org.omg.CosNaming.NamingContextPackage.InvalidName e) {
		e.printStackTrace();
	}
}

Теперь у нас есть ссылка на объект к которому мы можем обращаться вызывая его методы. В частности метод get_alarm_list и возвращает нужный нам список. Вот его поисание из 3GPP:
This method returns Alarm Informations. If flag is
TRUE, all returned Alarm Informations shall be in AlarmInformationSeq
that contains 0,1 or more Alarm Informations. Output parameter iter
shall be useless. If flag is FALSE, no Alarm Information shall be
in AlarmInformationSeq. IRPAgent needs to use iter to retrieve them.

public void getActiveAlarms(){
	BooleanHolder flag = new BooleanHolder(false);  // false for iteration
	com.ericsson.irp.AlarmIRPSystem.AlarmInformationIteratorHolder iter = new com.ericsson.irp.AlarmIRPSystem.AlarmInformationIteratorHolder();
	try {
		_alarmIrp.get_alarm_list(alarmFilter, flag, iter);
		EventBatchHolder alarmInformation = new EventBatchHolder();
		short alarmSize = 100;
		List<StructuredEvent> alarms = new ArrayList<StructuredEvent>();
		boolean haveMoreAlarms = false;
		do{
			if (iter.value != null) {
				haveMoreAlarms = iter.value.next_alarmInformations(alarmSize, alarmInformation);
				alarms.addAll(Arrays.asList(alarmInformation.value));
			}
		}while (haveMoreAlarms);
			for (StructuredEvent alarm: alarms) {
				alarmPrint(alarm);
			}
		}
	} catch (GetAlarmList | ParameterNotSupported | InvalidParameter | NextAlarmInformations e) {
		e.printStackTrace();
	}
}

Данный метод получает итератор содержащий список аварий в виде объектов типа StructuredEvent которые затем выводит в консоль alarmPrint(alarm). Запись StructuredEvent содержит заголовок header и, собственно, данные filterable_data. Данные это тоже запись имеющая имя name и значение value. Описание полей тоже есть в стандарте:
const string NV_NOTIFICATION_ID =«a»;
const string NV_CORRELATED_NOTIFICATIONS = «b»;
const string NV_EVENT_TIME = «c»;
const string NV_SYSTEM_DN = «d»;
const string NV_MANAGED_OBJECT_CLASS = «e»;
const string NV_MANAGED_OBJECT_INSTANCE= «f»;
const string NV_SPECIFIC_PROBLEM = «i»;


Теперь всё это соберём вместе и выведем для примера instance и specific_problem:

private void alarmPrint(StructuredEvent alarm){
	String result = ""
	if (alarm.filterable_data != null) {
		for (Property filterableData: alarm.filterable_data) {
			String fieldName = filterableData.name;
			switch (fieldName){
				case "f":
					result = result + filterableData.value.extract_string() + ";";
					break;
				case "i":
					result = result + filterableData.value.extract_string();
					break;
			}
		}
	}
	System.out.println(result);
}

Наконец полный код полученного черновика:

Полный код
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Properties;
import org.omg.CORBA.*;
import org.omg.CORBA.ORBPackage.InvalidName;
import org.omg.CosNaming.NamingContextExt;
import org.omg.CosNaming.NamingContextExtHelper;
import org.omg.CosNaming.NamingContextPackage.CannotProceed;
import org.omg.CosNaming.NamingContextPackage.NotFound;
import org.omg.CosNotification.EventBatchHolder;
import org.omg.CosNotification.Property;
import org.omg.CosNotification.StructuredEvent;
import com.ericsson.irp.AlarmIRPSystem.GetAlarmList;
import com.ericsson.irp.AlarmIRPSystem.InvalidParameter;
import com.ericsson.irp.AlarmIRPSystem.NextAlarmInformations;
import com.ericsson.irp.AlarmIRPSystem.ParameterNotSupported;


public class AlarmClient {

	private com.ericsson.irp.AlarmIRPSystem._AlarmIRPOperations _alarmIrp = null;
	
	public static void main(String[] args) {
		AlarmClient ac = new AlarmClient();
		ac.createAlarmObj();
		ac.getActiveAlarms();
	}
	
	private String readIOR() {
        File f = new File("/var/opt/ericsson/blkcm/data/bulkcm.nameservice");
        BufferedReader br;
        String iorContents = null;
		try {
			br = new BufferedReader(new FileReader(f));
	        iorContents = br.readLine();
	        br.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
        return iorContents;
	}
	
	public void createAlarmObj(){
		org.omg.CORBA.Object rootObj = null;
		NamingContextExt rootNameCon = null;
		Properties props = new Properties();
		props.put("org.omg.CORBA.ORBInitRef", "NameService=" + readIOR());
		org.omg.CORBA.ORB orb = ORB.init(new String[0], props);
		// Resolve the CORBA Naming Service 
		try {
			rootObj = orb.resolve_initial_references("NameService");
			rootNameCon = NamingContextExtHelper.narrow(rootObj);
			String s = "com/ericsson/nms/fm_cirpagent/AlarmIRP";
			//Locate Alarm IRP
			rootObj = rootNameCon.resolve(rootNameCon.to_name(s));
			_alarmIrp = com.ericsson.irp.AlarmIRPSystem._AlarmIRPOperationsHelper.narrow(rootObj);
			//System.out.println(_alarmIrp);
		} catch (InvalidName | NotFound | CannotProceed | org.omg.CosNaming.NamingContextPackage.InvalidName e) {
			e.printStackTrace();
		}
	}

	public void getActiveAlarms(){
		BooleanHolder flag = new BooleanHolder(false);  // false for iteration
		com.ericsson.irp.AlarmIRPSystem.AlarmInformationIteratorHolder iter = new com.ericsson.irp.AlarmIRPSystem.AlarmInformationIteratorHolder();
		try {
			_alarmIrp.get_alarm_list("", flag, iter);
			EventBatchHolder alarmInformation = new EventBatchHolder();
			short alarmSize = 100;
			List<StructuredEvent> alarms = new ArrayList<StructuredEvent>();
			boolean haveMoreAlarms = false;
			do{
				if (iter.value != null) {
					haveMoreAlarms = iter.value.next_alarmInformations(alarmSize, alarmInformation);
					alarms.addAll(Arrays.asList(alarmInformation.value));
				}
			}while (haveMoreAlarms);
			if (iter.value != null) {
				for (StructuredEvent alarm: alarms) {
					alarmPrint(alarm);
				}
			}
		} catch (GetAlarmList | ParameterNotSupported | InvalidParameter | NextAlarmInformations e) {
			e.printStackTrace();
		}
	}
	
	private void alarmPrint(StructuredEvent alarm){
		String result = "";
		if (alarm.filterable_data != null) {
			for (Property filterableData: alarm.filterable_data) {
				String fieldName = filterableData.name;
				switch (fieldName){
					case "f":
						result = result + filterableData.value.extract_string() + ";";
						break;
					case "i":
						result = result + filterableData.value.extract_string();
						break;
				}
			}
		}
		System.out.println(result);
	}
}


Запуск производится командой:
java -cp .:/opt/ericsson/fm_core/classes/alarmirp.jar AlarmClient

Что в итоге: кроме «фана» я других применений пока не использую. В перспективе есть ещё NotificationIRP — получение событий сразу после их появления, BulkCmIRP — конфигурирование из внешней системы и т.д. Технология аналогичная, но при необходимости можно написать отдельную статью. По этой теме, пожалуй, всё. На вопросы могу ответить в комментариях. Спасибо!

Комментарии (2)


  1. sensem
    06.09.2018 16:50

    Спасибо за статью. Как человек, бившийся с CORBA почти год, узнал во многом себя.
    Жаль, Вы не добрались до вывода NV_EVENT_TIME: в этом поле зашит сюрприз в виде огромного целого числа, эквивалентного сотням наносекунд(!), прошедших с 15 октября 1582 года. Пришлось пилить метод для преобразования этого числа в нормальную дату:

    public static Date getNormalDate(long timeCORBA) {
    	return new Date(timeCORBA/10000L - 12219292800000L);
    }
    

    Есть очень много интересных вещей, связанных с CORBA у Ericsson, Huawei и Nokia (особенно те, что касаются NotificationIRP).


    1. Tinkz Автор
      06.09.2018 17:08

      Спасибо за отзыв, дату я выводил:

      timeValue = UtcTHelper.extract(filterableData.value);
      dt = new Date((timeValue.time - 122192928000000000L) / 10000);

      NotificationIRP в итоге тоже победил, но да, пришлось повозиться.