Beispiel #1
0
 def test_bad_get_north_plugin_config(self, exc_name, log_exc_name, msg):
     with patch.object(_logger, log_exc_name) as patch_log_exc:
         with patch.object(common, 'load_and_fetch_python_plugin_info', side_effect=[exc_name]):
             PluginDiscovery.get_plugin_config("http", "north", False)
     assert 1 == patch_log_exc.call_count
     args, kwargs = patch_log_exc.call_args
     assert msg in args[0]
Beispiel #2
0
 def test_fetch_c_hybrid_plugins_installed(self):
     info = {
         "version": "1.6.0",
         "name": "FlirAX8",
         "config": {
             "asset": {
                 "description": "Default asset name",
                 "default": "flir",
                 "displayName": "Asset Name",
                 "type": "string"
             },
             "plugin": {
                 "description":
                 "A Modbus connected Flir AX8 infrared camera",
                 "default": "FlirAX8",
                 "readonly": "true",
                 "type": "string"
             }
         }
     }
     with patch.object(utils,
                       "find_c_plugin_libs",
                       return_value=[("FlirAX8", "json")
                                     ]) as patch_plugin_lib:
         with patch.object(common,
                           "load_and_fetch_c_hybrid_plugin_info",
                           return_value=info) as patch_hybrid_plugin_info:
             PluginDiscovery.fetch_c_plugins_installed('south', True)
         patch_hybrid_plugin_info.assert_called_once_with(
             info['name'], True)
     patch_plugin_lib.assert_called_once_with('south')
Beispiel #3
0
 def test_bad_fetch_c_north_plugin_installed(self, info, exc_count):
     with patch.object(_logger, "exception") as patch_log_exc:
         with patch.object(utils, "find_c_plugin_libs", return_value=[("PI_Server", "binary")]) as patch_plugin_lib:
             with patch.object(utils, "get_plugin_info", return_value=info) as patch_plugin_info:
                 PluginDiscovery.fetch_c_plugins_installed("north", False)
             patch_plugin_info.assert_called_once_with('PI_Server', dir='north')
         patch_plugin_lib.assert_called_once_with('north')
         assert exc_count == patch_log_exc.call_count
Beispiel #4
0
 def test_deprecated_python_plugins(self, info, warn_count, is_config=True):
     with patch.object(_logger, "warning") as patch_log_warn:
         with patch.object(common, 'load_and_fetch_python_plugin_info', side_effect=[info]):
             PluginDiscovery.get_plugin_config(info['name'], info['type'], is_config)
     assert warn_count == patch_log_warn.call_count
     if warn_count:
         args, kwargs = patch_log_warn.call_args
         assert '"{}" plugin is deprecated'.format(info['name']) == args[0]
Beispiel #5
0
 def test_fetch_c_plugins_installed(self, info, dir_name):
     with patch.object(utils,
                       "find_c_plugin_libs",
                       return_value=[(info['name'], "binary")
                                     ]) as patch_plugin_lib:
         with patch.object(utils, "get_plugin_info",
                           return_value=info) as patch_plugin_info:
             PluginDiscovery.fetch_c_plugins_installed(dir_name, True)
         patch_plugin_info.assert_called_once_with(info['name'],
                                                   dir=dir_name)
     patch_plugin_lib.assert_called_once_with(dir_name)
Beispiel #6
0
 def test_deprecated_c_plugins_installed(self, info, dir_name):
     info['flag'] = api_utils.DEPRECATED_BIT_MASK_VALUE
     with patch.object(_logger, "warning") as patch_log_warn:
         with patch.object(utils, "find_c_plugin_libs", return_value=[(info['name'], "binary")]) as patch_plugin_lib:
             with patch.object(utils, "get_plugin_info", return_value=info) as patch_plugin_info:
                 PluginDiscovery.fetch_c_plugins_installed(dir_name, True)
             patch_plugin_info.assert_called_once_with(info['name'], dir=dir_name)
         patch_plugin_lib.assert_called_once_with(dir_name)
     assert 1 == patch_log_warn.call_count
     args, kwargs = patch_log_warn.call_args
     assert '"{}" plugin is deprecated'.format(info['name']) == args[0]
Beispiel #7
0
 def test_get_plugin_config(self, info, expected, is_config):
     with patch.object(common,
                       'load_and_fetch_python_plugin_info',
                       side_effect=[info]):
         actual = PluginDiscovery.get_plugin_config("modbus", "south",
                                                    is_config)
         assert expected == actual
Beispiel #8
0
    def test_get_rules_plugins_installed(self, mocker):
        @asyncio.coroutine
        def mock_folders():
            yield TestPluginDiscovery.mock_py_rule_folders

        @asyncio.coroutine
        def mock_c_folders():
            yield TestPluginDiscovery.mock_c_rule_folders

        mock_get_py_folders = mocker.patch.object(PluginDiscovery,
                                                  "get_plugin_folders",
                                                  return_value=next(
                                                      mock_folders()))
        mock_get_py_plugin_config = mocker.patch.object(
            PluginDiscovery,
            "get_plugin_config",
            side_effect=TestPluginDiscovery.mock_py_rule_config)
        mock_get_c_folders = mocker.patch.object(utils,
                                                 "find_c_plugin_libs",
                                                 return_value=next(
                                                     mock_c_folders()))
        mock_get_c_plugin_config = mocker.patch.object(
            utils,
            "get_plugin_info",
            side_effect=TestPluginDiscovery.mock_c_rule_config)

        plugins = PluginDiscovery.get_plugins_installed("notificationRule")
        # expected_plugin = TestPluginDiscovery.mock_c_plugins_config[4]
        # FIXME: ordering issue
        # assert expected_plugin == plugins
        assert 1 == mock_get_py_folders.call_count
        assert 1 == mock_get_py_plugin_config.call_count
        assert 1 == mock_get_c_folders.call_count
        assert 1 == mock_get_c_plugin_config.call_count
Beispiel #9
0
    def test_get_plugins_installed_type_south(self, mocker):
        @asyncio.coroutine
        def mock_folders():
            yield TestPluginDiscovery.mock_south_folders

        @asyncio.coroutine
        def mock_c_folders():
            yield TestPluginDiscovery.mock_c_south_folders

        mock_get_folders = mocker.patch.object(PluginDiscovery,
                                               "get_plugin_folders",
                                               return_value=next(
                                                   mock_folders()))
        mock_get_plugin_config = mocker.patch.object(
            PluginDiscovery,
            "get_plugin_config",
            side_effect=TestPluginDiscovery.mock_plugins_south_config)
        mock_get_c_folders = mocker.patch.object(utils,
                                                 "find_c_plugin_libs",
                                                 return_value=next(
                                                     mock_c_folders()))
        mock_get_c_plugin_config = mocker.patch.object(
            utils,
            "get_plugin_info",
            side_effect=TestPluginDiscovery.mock_c_plugins_south_config)

        plugins = PluginDiscovery.get_plugins_installed("south")
        expected_plugin = TestPluginDiscovery.mock_plugins_south_config
        expected_plugin.extend(TestPluginDiscovery.mock_c_plugins_south_config)
        # FIXME: ordering issue
        # assert expected_plugin == plugins
        assert 1 == mock_get_folders.call_count
        assert 2 == mock_get_plugin_config.call_count
        assert 1 == mock_get_c_folders.call_count
        assert 1 == mock_get_c_plugin_config.call_count
Beispiel #10
0
async def remove_plugin(request):
    """ Remove installed plugin from fledge

    type: installed plugin type
    name: installed plugin name

    Example:
        curl -X DELETE http://localhost:8081/fledge/plugins/south/sinusoid
        curl -X DELETE http://localhost:8081/fledge/plugins/north/http_north
        curl -X DELETE http://localhost:8081/fledge/plugins/filter/expression
        curl -X DELETE http://localhost:8081/fledge/plugins/notificationDelivery/alexa
        curl -X DELETE http://localhost:8081/fledge/plugins/notificationRule/Average
    """
    plugin_type = request.match_info.get('type', None)
    name = request.match_info.get('name', None)
    try:
        plugin_type = str(plugin_type).lower() if not str(plugin_type).startswith('notification') else plugin_type
        if plugin_type not in valid_plugin_types:
            raise ValueError("Invalid plugin type. Please provide valid type: {}".format(valid_plugin_types))
        installed_plugin = PluginDiscovery.get_plugins_installed(plugin_type, False)
        if name not in [plugin['name'] for plugin in installed_plugin]:
            raise KeyError("Invalid plugin name {} or plugin is not installed".format(name))
        if plugin_type in ['notificationDelivery', 'notificationRule']:
            notification_instances_plugin_used_in = await check_plugin_usage_in_notification_instances(name)
            if notification_instances_plugin_used_in:
                raise RuntimeError("{} cannot be removed. This is being used by {} instances".
                                   format(name, notification_instances_plugin_used_in))
            plugin_type = 'notify' if plugin_type == 'notificationDelivery' else 'rule'
        else:
            get_tracked_plugins = await check_plugin_usage(plugin_type, name)
            if get_tracked_plugins:
                e = "{} cannot be removed. This is being used by {} instances".\
                    format(name, get_tracked_plugins[0]['service_list'])
                _logger.error(e)
                raise RuntimeError(e)
            else:
                _logger.info("No entry found for {name} plugin in asset tracker; or "
                             "{name} plugin may have been added in disabled state & never used".format(name=name))
        res, log_path, is_package = purge_plugin(plugin_type, name)
        if res != 0:
            e_msg = "Something went wrong. Please check log {}".format(log_path)
            _logger.error(e_msg)
            raise RuntimeError(e_msg)
        else:
            if is_package:
                storage_client = connect.get_storage_async()
                audit_log = AuditLogger(storage_client)
                audit_detail = {'package_name': "fledge-{}-{}".format(plugin_type, name)}
                await audit_log.information('PKGRM', audit_detail)
    except (ValueError, RuntimeError) as ex:
        raise web.HTTPBadRequest(reason=str(ex))
    except KeyError as ex:
        raise web.HTTPNotFound(reason=str(ex))
    except PackageError as e:
        msg = "Failed to remove package for plugin {}".format(name)
        raise web.HTTPBadRequest(body=json.dumps({"message": msg, "link": str(e)}), reason=msg)
    else:
        _logger.info('{} plugin removed successfully'.format(name))
        return web.json_response({'message': '{} plugin removed successfully'.format(name)}, status=200)
Beispiel #11
0
async def update_plugin(request: web.Request) -> web.Response:
    """ update plugin

    :Example:
        curl -X PUT http://localhost:8081/fledge/plugins/south/sinusoid/update
        curl -X PUT http://localhost:8081/fledge/plugins/north/http_north/update
    """
    _type = request.match_info.get('type', None)
    name = request.match_info.get('name', None)
    try:
        # TODO: FOGL-3063, FOGL-3064
        _type = _type.lower()
        if _type not in ['north', 'south']:
            raise ValueError("Invalid plugin type. Must be 'north' or 'south'")

        # Check requested plugin name is installed or not
        installed_plugins = PluginDiscovery.get_plugins_installed(_type, False)
        installed_plugin_name = [
            p_name["name"] for p_name in installed_plugins
        ]
        if name not in installed_plugin_name:
            raise KeyError(
                "{} plugin is not yet installed. So update is not possible.".
                format(name))

        # Tracked plugins from asset tracker
        tracked_plugins = await _get_plugin_and_sch_name_from_asset_tracker(
            _type)
        sch_list = []
        for p in tracked_plugins:
            if name == p['plugin']:
                sch_info = await _get_sch_id_and_enabled_by_name(p['service'])
                if sch_info[0]['enabled'] == 't':
                    status, reason = await server.Server.scheduler.disable_schedule(
                        uuid.UUID(sch_info[0]['id']))
                    if status:
                        _logger.warning(
                            "{} {} instance is disabled as {} plugin is updating.."
                            .format(p['service'], _type, p['plugin']))
                        sch_list.append(sch_info[0]['id'])

        # Plugin update is running as a background task
        loop = request.loop
        request._type = _type
        request._name = name
        request._sch_list = sch_list
        loop.call_later(1, do_update, request)
    except KeyError as ex:
        raise web.HTTPNotFound(reason=ex)
    except ValueError as ex:
        raise web.HTTPBadRequest(reason=ex)
    except Exception as ex:
        raise web.HTTPInternalServerError(reason=ex)

    return web.json_response({
        "message":
        "{} plugin update in process. Wait for few minutes to complete.".
        format(name)
    })
Beispiel #12
0
    def test_get_plugin_folders(self, mocker):
        @asyncio.coroutine
        def mock_folders():
            listdir = copy.deepcopy(TestPluginDiscovery.mock_north_folders)
            listdir.extend(["__init__", "empty", "common"])
            yield listdir

        mocker.patch.object(os, "listdir", return_value=next(mock_folders()))
        mocker.patch.object(os.path, "isdir", return_value=True)
        plugin_folders = PluginDiscovery.get_plugin_folders("north")
        actual_plugin_folders = []
        for dir_name in plugin_folders:
            actual_plugin_folders.append(dir_name.split('/')[-1])
        assert TestPluginDiscovery.mock_north_folders == actual_plugin_folders
Beispiel #13
0
    def test_fetch_plugins_installed(self, mocker):
        @asyncio.coroutine
        def mock_folders():
            yield TestPluginDiscovery.mock_north_folders

        mock_get_folders = mocker.patch.object(PluginDiscovery, "get_plugin_folders", return_value=next(mock_folders()))
        mock_get_plugin_config = mocker.patch.object(PluginDiscovery, "get_plugin_config",
                                                     side_effect=TestPluginDiscovery.mock_plugins_north_config)

        plugins = PluginDiscovery.fetch_plugins_installed("north", False)
        # FIXME: below line is failing when in suite
        # assert TestPluginDiscovery.mock_plugins_north_config == plugins
        assert 1 == mock_get_folders.call_count
        assert 2 == mock_get_plugin_config.call_count
Beispiel #14
0
 def test_bad_get_plugin_config(self):
     mock_plugin_info = {
             'name': "HTTP",
             'version': "1.0.0",
             'type': "north",
             'interface': "1.0.0",
             'config': {
                         'plugin': {
                             'description': "HTTP north plugin",
                             'type': 'string',
                             'default': 'http-north'
                         }
             }
     }
     with patch.object(_logger, "warning") as patch_log_warn:
         with patch.object(common, 'load_and_fetch_python_plugin_info', side_effect=[mock_plugin_info]):
             actual = PluginDiscovery.get_plugin_config("http-north", "south", False)
             assert actual is None
     patch_log_warn.assert_called_once_with('Plugin http-north is discarded due to invalid type')
Beispiel #15
0
async def get_plugins_installed(request):
    """ get list of installed plugins

    :Example:
        curl -X GET http://localhost:8081/fledge/plugins/installed
        curl -X GET http://localhost:8081/fledge/plugins/installed?config=true
        curl -X GET http://localhost:8081/fledge/plugins/installed?type=north|south|filter|notificationDelivery|notificationRule
        curl -X 'GET http://localhost:8081/fledge/plugins/installed?type=north&config=true'
    """

    plugin_type = None
    is_config = False
    if 'type' in request.query and request.query['type'] != '':
        plugin_type = request.query['type'].lower(
        ) if request.query['type'] not in [
            'notificationDelivery', 'notificationRule'
        ] else request.query['type']

    if plugin_type is not None and plugin_type not in [
            'north', 'south', 'filter', 'notificationDelivery',
            'notificationRule'
    ]:
        raise web.HTTPBadRequest(
            reason=
            "Invalid plugin type. Must be 'north' or 'south' or 'filter' or 'notificationDelivery' or 'notificationRule'."
        )

    if 'config' in request.query:
        config = request.query['config']
        if config not in ['true', 'false', True, False]:
            raise web.HTTPBadRequest(reason='Only "true", "false", true, false'
                                     ' are allowed for value of config.')
        is_config = True if (
            (type(config) is str and config.lower() in ['true']) or
            ((type(config) is bool and config is True))) else False

    plugins_list = PluginDiscovery.get_plugins_installed(
        plugin_type, is_config)

    return web.json_response({"plugins": plugins_list})
Beispiel #16
0
async def update_plugin(request: web.Request) -> web.Response:
    """ update plugin

    :Example:
        curl -sX PUT http://localhost:8081/fledge/plugins/south/sinusoid/update
        curl -sX PUT http://localhost:8081/fledge/plugins/north/http_north/update
        curl -sX PUT http://localhost:8081/fledge/plugins/filter/metadata/update
        curl -sX PUT http://localhost:8081/fledge/plugins/notificationDelivery/asset/update
        curl -sX PUT http://localhost:8081/fledge/plugins/notificationRule/OutOfBound/update
    """
    _type = request.match_info.get('type', None)
    name = request.match_info.get('name', None)
    try:
        _type = _type.lower(
        ) if not str(_type).startswith('notification') else _type
        if _type not in [
                'north', 'south', 'filter', 'notificationDelivery',
                'notificationRule'
        ]:
            raise ValueError(
                "Invalid plugin type. Must be 'north', 'south', 'filter', 'notificationDelivery' "
                "or 'notificationRule'")

        # Check requested plugin name is installed or not
        installed_plugins = PluginDiscovery.get_plugins_installed(_type, False)
        installed_plugin_name = [
            p_name["name"] for p_name in installed_plugins
        ]
        if name not in installed_plugin_name:
            raise KeyError(
                "{} plugin is not yet installed. So update is not possible.".
                format(name))

        sch_list = []
        notification_list = []
        if _type in ['notificationDelivery', 'notificationRule']:
            # Check Notification service is enabled or not
            payload = PayloadBuilder().SELECT(
                "id", "enabled",
                "schedule_name").WHERE(['process_name', '=',
                                        'notification_c']).payload()
            storage_client = connect.get_storage_async()
            result = await storage_client.query_tbl_with_payload(
                'schedules', payload)
            sch_info = result['rows']
            if sch_info and sch_info[0]['enabled'] == 't':
                # Find notification instances which are used by requested plugin name
                # If its config item 'enable' is true then update to false
                config_mgr = ConfigurationManager(storage_client)
                all_notifications = await config_mgr._read_all_child_category_names(
                    "Notifications")
                for notification in all_notifications:
                    notification_config = await config_mgr._read_category_val(
                        notification['child'])
                    notification_name = notification_config['name']['value']
                    channel = notification_config['channel']['value']
                    rule = notification_config['rule']['value']
                    is_enabled = True if notification_config['enable'][
                        'value'] == 'true' else False
                    if (channel == name and is_enabled) or (rule == name
                                                            and is_enabled):
                        _logger.warning(
                            "Disabling {} notification instance, as {} {} plugin is updating..."
                            .format(notification_name, name, _type))
                        await config_mgr.set_category_item_value_entry(
                            notification_name, "enable", "false")
                        notification_list.append(notification_name)
            _type = "notify" if _type == 'notificationDelivery' else "rule"
        else:
            # Tracked plugins from asset tracker
            tracked_plugins = await _get_plugin_and_sch_name_from_asset_tracker(
                _type)
            filters_used_by = []
            if _type == 'filter':
                # In case of filter, for asset_tracker table we are inserting filter category_name in plugin column
                # instead of filter plugin name by Design
                # Hence below query is required to get actual plugin name from filters table
                storage_client = connect.get_storage_async()
                payload = PayloadBuilder().SELECT("name").WHERE(
                    ['plugin', '=', name]).payload()
                result = await storage_client.query_tbl_with_payload(
                    'filters', payload)
                filters_used_by = [r['name'] for r in result['rows']]
            for p in tracked_plugins:
                if (name == p['plugin'] and not _type == 'filter') or (
                        p['plugin'] in filters_used_by and _type == 'filter'):
                    sch_info = await _get_sch_id_and_enabled_by_name(
                        p['service'])
                    if sch_info[0]['enabled'] == 't':
                        status, reason = await server.Server.scheduler.disable_schedule(
                            uuid.UUID(sch_info[0]['id']))
                        if status:
                            _logger.warning(
                                "Disabling {} {} instance, as {} plugin is updating..."
                                .format(p['service'], _type, name))
                            sch_list.append(sch_info[0]['id'])

        # Plugin update is running as a background task
        loop = request.loop
        request._type = _type
        request._name = name
        request._sch_list = sch_list
        request._notification_list = notification_list
        loop.call_later(1, do_update, request)
    except KeyError as ex:
        raise web.HTTPNotFound(reason=str(ex))
    except ValueError as ex:
        raise web.HTTPBadRequest(reason=str(ex))
    except Exception as ex:
        raise web.HTTPInternalServerError(reason=str(ex))

    return web.json_response({
        "message":
        "{} plugin update in process. Wait for few minutes to complete.".
        format(name)
    })
Beispiel #17
0
def _get_installed_plugins():
    return PluginDiscovery.get_plugins_installed("south", False)