auth_provider = oauth2
+# The minimal retention period. By default is 14, minimal support is 1.
+#min_retention_period = 14
+
[OAUTH2]
# support OAuth2.0
.AlarmSubscriptionSqlAlchemyRepository(self.session)
self.alarm_probable_causes = alarm_repository\
.AlarmProbableCauseSqlAlchemyRepository(self.session)
+ self.alarm_service_config = alarm_repository\
+ .AlarmServiceConfigurationSqlAlchemyRepository(self.session)
return super().__enter__()
from retry import retry
import inspect
-from typing import Callable
+from typing import Callable, Optional
from o2common.adapter.notifications import AbstractNotifications,\
NoneNotifications
def bootstrap(
start_orm: bool = True,
uow: unit_of_work.AbstractUnitOfWork = SqlAlchemyUnitOfWork(),
- notifications: AbstractNotifications = None,
+ notifications: Optional[AbstractNotifications] = None,
publish: Callable = redis_eventpublisher.publish,
) -> messagebus.MessageBus:
-
- if notifications is None:
- notifications = NoneNotifications()
+ """
+ Bootstrap the application with dependencies.
+ """
+ notifications = notifications or NoneNotifications()
if start_orm:
with uow:
- # get default engine if uow is by default
engine = uow.session.get_bind()
-
wait_for_db_ready(engine)
o2ims_orm.start_o2ims_mappers(engine)
o2dms_orm.start_o2dms_mappers(engine)
- dependencies = {"uow": uow, "notifications": notifications,
- "publish": publish}
+ dependencies = {
+ "uow": uow,
+ "notifications": notifications,
+ "publish": publish
+ }
injected_event_handlers = {
event_type: [
inject_dependencies(handler, dependencies)
_DCMANAGER_URL_PORT = os.environ.get("DCMANAGER_API_PORT", "8119")
_DCMANAGER_URL_PATH = os.environ.get("DCMANAGER_API_PATH", "/v1.0")
+_DEFAULT_MIN_RETENTION_PERIOD = 14
+_MIN_MIN_RETENTION_PERIOD = 1
+
def get_config_path():
path = os.environ.get("O2APP_CONFIG", "/configs/o2app.conf")
def get_auth_provider():
- return config.conf.auth_provider
+ return config.conf.DEFAULT.auth_provider
def get_dms_support_profiles():
if 'native_k8sapi' not in profiles_list:
profiles_list.append('native_k8sapi')
return profiles_list
+
+
+def get_min_retention_period():
+ try:
+ min_retention_period_str = config.conf.DEFAULT.min_retention_period
+ if min_retention_period_str is not None:
+ min_retention_period_int = int(min_retention_period_str)
+ if min_retention_period_int >= _MIN_MIN_RETENTION_PERIOD:
+ return min_retention_period_int
+ except (ValueError, TypeError) as e:
+ logger.warning(f"Invalid min_retention_period value: {e}")
+
+ return _DEFAULT_MIN_RETENTION_PERIOD
from o2ims.domain import alarm_obj
from o2ims.domain.alarm_repo import AlarmDefinitionRepository, \
AlarmEventRecordRepository, AlarmSubscriptionRepository, \
- AlarmProbableCauseRepository, AlarmDictionaryRepository
+ AlarmProbableCauseRepository, AlarmDictionaryRepository, \
+ AlarmServiceConfigurationRepository
from o2common.helper import o2logging
logger = o2logging.get_logger(__name__)
def _delete(self, probable_cause_id):
self.session.query(alarm_obj.ProbableCause).filter_by(
probableCauseId=probable_cause_id).delete()
+
+
+class AlarmServiceConfigurationSqlAlchemyRepository(
+ AlarmServiceConfigurationRepository):
+ def __init__(self, session):
+ super().__init__()
+ self.session = session
+
+ def _add_default(self) -> alarm_obj.AlarmServiceConfiguration:
+ query = self.session.query(
+ alarm_obj.AlarmServiceConfiguration).first()
+ if not query:
+ default_config = alarm_obj.AlarmServiceConfiguration(
+ retention_period=14
+ )
+ self.session.add(default_config)
+ self.session.commit()
+ logger.info(
+ "Inserted default AlarmServiceConfiguration record.")
+ return default_config
+ return query
+
+ def _get(self) -> alarm_obj.AlarmServiceConfiguration:
+ return self.session.query(alarm_obj.AlarmServiceConfiguration).first()
+
+ def _update(self, service_config: alarm_obj.AlarmServiceConfiguration):
+ print(service_config.retentionPeriod)
+ self.session.merge(service_config)
Column("filter", String(255)),
)
+alarm_service_configuration = Table(
+ "alarmServiceConfiguration",
+ metadata,
+ Column("updatetime", DateTime),
+ Column("createtime", DateTime),
+
+ Column("id", Integer, primary_key=True, autoincrement=True),
+ Column("retentionPeriod", Integer, default=15)
+)
+
@retry((exc.IntegrityError), tries=3, delay=2)
def wait_for_metadata_ready(engine):
logger.info("Starting O2 IMS mappers")
# IMS Infrastruture Monitoring Mappering
+ mapper(alarmModel.AlarmServiceConfiguration, alarm_service_configuration)
mapper(alarmModel.AlarmEventRecord, alarm_event_record)
alarmdefinition_mapper = mapper(
alarmModel.AlarmDefinition, alarm_definition)
self.perceivedSeverity = clear
+class AlarmServiceConfiguration(AgRoot, Serializer):
+ def __init__(self, retention_period: int = None) -> None:
+ super().__init__()
+ self.retentionPeriod = retention_period
+
+
class AlarmDefinition(AgRoot, Serializer):
def __init__(self, id: str, name: str, change_type: AlarmChangeTypeEnum,
desc: str, prop_action: str, clearing_type: ClearingTypeEnum,
@abc.abstractmethod
def _delete(self, probable_cause_id):
raise NotImplementedError
+
+
+class AlarmServiceConfigurationRepository(abc.ABC):
+ def __init__(self):
+ self.seen = set() # type: Set[obj.AlarmServiceConfiguration]
+
+ def get(self) -> obj.AlarmServiceConfiguration:
+ service_config = self._get()
+ if service_config:
+ self.seen.add(service_config)
+ else:
+ service_config = self._add_default()
+ return service_config
+
+ def update(self, service_config: obj.AlarmServiceConfiguration):
+ self._update(service_config)
+
+ @abc.abstractmethod
+ def _add_default(self) -> obj.AlarmServiceConfiguration:
+ raise NotImplementedError
+
+ @abc.abstractmethod
+ def _get(self) -> obj.AlarmServiceConfiguration:
+ raise NotImplementedError
+
+ @abc.abstractmethod
+ def _update(self, service_config: obj.AlarmServiceConfiguration):
+ raise NotImplementedError
'provided then all events are reported.'),
}
)
+
+
+class AlarmServiceConfigurationDTO:
+
+ alarm_service_configuration_get = api_monitoring_v1.model(
+ "AlarmServiceConfigurationDto",
+ {
+ 'retentionPeriod': fields.Integer(
+ required=True,
+ example=14,
+ description='Number of days for alarm history to be retained.'
+ ),
+ 'extensions': Json2Dict(attribute='extensions')
+ }
+ )
+
+ alarm_service_configuration_expect = api_monitoring_v1.model(
+ "AlarmServiceConfigurationDto",
+ {
+ 'retentionPeriod': fields.Integer(
+ required=True,
+ example=14,
+ description='Number of days for alarm history to be retained.'
+ )
+ }
+ )
from flask import request
from flask_restx import Resource, reqparse
+from o2common.config import config
from o2common.service.messagebus import MessageBus
from o2common.views.pagination_route import link_header, PAGE_PARAM
from o2common.views.route_exception import NotFoundException, \
from o2ims.views import alarm_view
from o2ims.views.api_ns import api_ims_monitoring as api_monitoring_v1
from o2ims.views.alarm_dto import AlarmDTO, SubscriptionDTO, \
- MonitoringApiV1DTO
+ MonitoringApiV1DTO, AlarmServiceConfigurationDTO
from o2common.helper import o2logging
logger = o2logging.get_logger(__name__)
return {
'uriPrefix': request.base_url.rsplit('/', 1)[0],
'apiVersions': [{
- 'version': '1.0.0',
+ 'version': '1.1.0',
# 'isDeprecated': 'False',
# 'retirementDate': ''
}]
def delete(self, alarmSubscriptionID):
result = alarm_view.subscription_delete(alarmSubscriptionID, bus.uow)
return result, 200
+
+
+# ---------- Alarm Event Record ---------- #
+@api_monitoring_v1.route("/v1/alarmServiceConfiguration")
+@api_monitoring_v1.param(
+ 'all_fields',
+ 'Set any value for show all fields. This value will cover "fields" ' +
+ 'and "all_fields".',
+ _in='query')
+@api_monitoring_v1.param(
+ 'fields',
+ 'Set fields to show, split by comma, "/" for parent and children.' +
+ ' Like "name,parent/children". This value will cover' +
+ ' "exculde_fields".',
+ _in='query')
+@api_monitoring_v1.param(
+ 'exclude_fields',
+ 'Set fields to exclude showing, split by comma, "/" for parent and ' +
+ 'children. Like "name,parent/children". This value will cover ' +
+ '"exclude_default".',
+ _in='query')
+@api_monitoring_v1.param(
+ 'exclude_default',
+ 'Exclude showing all default fields, Set "true" to enable.',
+ _in='query')
+class AlarmServiceConfigurationRouter(Resource):
+
+ model = AlarmServiceConfigurationDTO.alarm_service_configuration_get
+ expect = AlarmServiceConfigurationDTO.alarm_service_configuration_expect
+
+ @api_monitoring_v1.doc('Get Alarm Service Configuration')
+ @api_monitoring_v1.marshal_with(model)
+ def get(self):
+ result = alarm_view.alarm_service_configuration(bus.uow)
+ if result is not None:
+ return result
+
+ @api_monitoring_v1.doc('Patch Alarm Service Configuration')
+ @api_monitoring_v1.expect(expect)
+ @api_monitoring_v1.marshal_with(model)
+ def patch(self):
+ data = api_monitoring_v1.payload
+ retention_period = data.get('retentionPeriod', None)
+
+ min_retention_period = config.get_min_retention_period()
+ print(min_retention_period)
+
+ if retention_period is None:
+ raise BadRequestException(
+ 'The "retentionPeriod" parameter is required')
+ elif retention_period < min_retention_period:
+ raise BadRequestException(
+ f'The "retentionPeriod" parameter shall more than '
+ f'{min_retention_period} days')
+
+ result = alarm_view.alarm_service_configuration_update(data, bus.uow)
+ if result is not None:
+ return result
+
+ raise BadRequestException(
+ 'Failed to update alarm service configuration')
+
+ @api_monitoring_v1.doc('Update Alarm Service Configuration')
+ @api_monitoring_v1.expect(expect)
+ @api_monitoring_v1.marshal_with(model)
+ def put(self):
+ data = api_monitoring_v1.payload
+ retention_period = data.get('retentionPeriod', None)
+
+ min_retention_period = config.get_min_retention_period()
+
+ if retention_period is None:
+ raise BadRequestException(
+ 'The "retentionPeriod" parameter is required')
+ elif retention_period < min_retention_period:
+ raise BadRequestException(
+ f'The "retentionPeriod" parameter shall more than '
+ f'{min_retention_period} days')
+
+ result = alarm_view.alarm_service_configuration_update(data, bus.uow)
+ if result is not None:
+ return result
+
+ raise BadRequestException(
+ 'Failed to update alarm service configuration')
uow.alarm_subscriptions.delete(subscriptionId)
uow.commit()
return True
+
+
+def alarm_service_configuration(uow: unit_of_work.AbstractUnitOfWork):
+ with uow:
+ first = uow.alarm_service_config.get()
+ return first.serialize() if first is not None else None
+
+
+def alarm_service_configuration_update(data: dict,
+ uow: unit_of_work.AbstractUnitOfWork):
+ with uow:
+ first = uow.alarm_service_config.get()
+ first.retentionPeriod = data.get('retentionPeriod')
+ uow.alarm_service_config.update(first)
+ uow.commit()
+ return first.serialize()
return {
'uriPrefix': request.base_url.rsplit('/', 1)[0],
'apiVersions': [{
- 'version': '1.0.0',
+ 'version': '1.1.0',
# 'isDeprecated': 'False',
# 'retirementDate': ''
}]
resp = client.delete(uri)
assert resp.status == '405 METHOD NOT ALLOWED'
+ # Testing alarm service configuration not support method
+ ##########################
+ uri = apibase + "/alarmServiceConfiguration"
+ resp = client.post(uri)
+ assert resp.status == '405 METHOD NOT ALLOWED'
+ resp = client.delete(uri)
+ assert resp.status == '405 METHOD NOT ALLOWED'
+
class FakeAlarmClient(BaseClient):
def __init__(self):
count3 = fakewatcher.fakeOcloudWatcherCounter
time.sleep(3)
assert fakewatcher.fakeOcloudWatcherCounter == count3
+
+
+def test_view_alarm_service_configuration(mock_uow):
+ session, uow = mock_uow
+
+ # Test the first time call the view will generate the default data
+ session.return_value.query.return_value.first.return_value = None
+ result = alarm_view.alarm_service_configuration(uow)
+ assert result is not None
+ assert result.get("retentionPeriod") == 14
+
+ config1 = MagicMock()
+ config1.serialize.return_value = {
+ "id": 1,
+ "retentionPeriod": 30
+ }
+ session.return_value.query.return_value.first.return_value = config1
+
+ result = alarm_view.alarm_service_configuration(uow)
+ assert result is not None
+ assert result.get("id") == 1
+ assert result.get("retentionPeriod") == 30
+
+
+def test_view_alarm_service_configuration_update(mock_uow):
+ session, uow = mock_uow
+
+ config1 = MagicMock()
+ config1.serialize.return_value = {
+ "id": 1,
+ "retentionPeriod": 60
+ }
+ session.return_value.query.return_value.first.return_value = config1
+
+ # Test update the config
+ update_data = {
+ "retentionPeriod": 60
+ }
+ result = alarm_view.alarm_service_configuration_update(update_data, uow)
+
+ assert result is not None
+ assert result.get("id") == 1
+ assert result.get("retentionPeriod") == 60
+ # Verify the update and commit is called
+ session.return_value.query.return_value.first.return_value.\
+ retentionPeriod = 60
+ assert session.return_value.commit.called
+
+
+def test_flask_get_alarm_config(mock_flask_uow):
+ session, app = mock_flask_uow
+ apibase = config.get_o2ims_monitoring_api_base() + '/v1'
+
+ config1 = MagicMock()
+ config1.serialize.return_value = {
+ "retentionPeriod": 30
+ }
+
+ session.return_value.query.return_value.first.return_value = config1
+
+ with app.test_client() as client:
+ resp = client.get(apibase+"/alarmServiceConfiguration")
+ assert resp.status_code == 200
+ data = resp.get_json()
+ assert data["retentionPeriod"] == 30
+
+
+def test_flask_patch_alarm_config(mock_flask_uow):
+ session, app = mock_flask_uow
+ apibase = config.get_o2ims_monitoring_api_base() + '/v1'
+
+ config1 = MagicMock()
+ config1.serialize.return_value = {
+ "retentionPeriod": 60
+ }
+ session.return_value.query.return_value.first.return_value = config1
+
+ with app.test_client() as client:
+ # Test updating retention period
+ resp = client.patch(apibase+"/alarmServiceConfiguration", json={
+ "retentionPeriod": 60
+ })
+ assert resp.status_code == 200
+
+ # Test invalid retention period
+ resp = client.patch(apibase+"/alarmServiceConfiguration", json={
+ "retentionPeriod": -1
+ })
+ assert resp.status_code == 400
+
+
+def test_flask_put_alarm_config(mock_flask_uow):
+ session, app = mock_flask_uow
+ apibase = config.get_o2ims_monitoring_api_base() + '/v1'
+
+ config1 = MagicMock()
+ config1.serialize.return_value = {
+ "alarmServiceId": "alarm-service-1",
+ "retentionPeriod": 60
+ }
+ session.return_value.query.return_value.first.return_value = config1
+
+ with app.test_client() as client:
+ # Test updating retention period
+ resp = client.put(apibase+"/alarmServiceConfiguration", json={
+ "retentionPeriod": 60
+ })
+ assert resp.status_code == 200
+
+ # Test invalid retention period
+ resp = client.put(apibase+"/alarmServiceConfiguration", json={
+ "retentionPeriod": -1
+ })
+ assert resp.status_code == 400