Plugins are not applied to custom directive
Deployment Type
Docker
Version
v2.0.4
Steps to Reproduce
-
Create directives.yaml: juniper-bgp-route: name: BGP Route Custom plugins: - "/etc/hyperglass/plugins/transform_plugin.py" rules: - condition: 0.0.0.0/0 commands: show route protocol bgp table inet.0 {target} detail | display json - condition: ::/0 commands: show route protocol bgp table inet6.0 {target} detail | display json field: description: 'IPv4 or IPv6 Address' validation: '[0-9a-f.:/]+' juniper-received-from-peer: name: Received from Peer plugins: - /etc/hyperglass/custom_plugins/transform_plugin.py rules: - condition: '' commands: show route receive-protocol bgp {target}
field: description: 'Your IPv4 or IPv6 BGP Peer Address' validation: '[0-9a-f.:/]+'
-
Create plugin based on https://hyperglass.dev/plugins from ipaddress import ip_network from hyperglass.plugins import InputPlugin
class TransformCIDR(InputPlugin): def transform(self, query): (target := query.query_target) target_network = ip_network(target) if target_network.version == 4: return f"{target_network.network_address!s} {target_network.netmask!s}" return target
-
put the transform_plugin.py in /etc/hyperglass/plugins and /etc/hyperglass/custom_plugins folder
-
check that the container can see the plugins: docker container exec hyperglass-hyperglass-1 cat /etc/hyperglass/plugins/transform_plugin.py docker container exec hyperglass-hyperglass-1 cat /etc/hyperglass/custom_plugins/transform_plugin.py
-
enable debug mode like in https://hyperglass.dev/installation/environment-variables
-
enter the hyperglass site and enter "192.0.2.0/24" in the query as in https://hyperglass.dev/plugins example
-
observe the live output from the container and look at the line: hyperglass-1 | [DEBUG] 20241030 14:26:43 |129 | queries → Constructed query {'type': 'juniper-bgp-route', 'target': ['192.0.2.0/24'], 'constructed_query': ['show route protocol bgp table inet.0 192.0.2.0/24 detail | display json']}
-
look at the result in the webinterface, as it should transform 192.0.2.0/24 to 192.0.2.0 255.255.255.0 and the query is executed on a juniper device it should return a syntax error at the 255.255.255.0
Expected Behavior
I would expect that the constructed_query line form step 7. above would be: hyperglass-1 | [DEBUG] 20241030 14:26:43 |129 | queries → Constructed query {'type': 'juniper-bgp-route', 'target': ['192.0.2.0/24'], 'constructed_query': ['show route protocol bgp table inet.0 192.0.2.0 255.255.255.0 detail | display json']}
and the result in the webinterface should return a syntax error at the 255.255.255.0 as it is executed on a juniper device in my case
Observed Behavior
I get the line: hyperglass-1 | [DEBUG] 20241030 14:26:43 |129 | queries → Constructed query {'type': 'juniper-bgp-route', 'target': ['192.0.2.0/24'], 'constructed_query': ['show route protocol bgp table inet.0 192.0.2.0 255.255.255.0 detail | display json']}
and a normal result in the webinterface as if the query wasn't changed by the plugin
For me it seems like the plugin is not applied at all, even if change the last line in the plugin config to "return 'test'" the query is still successfull. I tried different folders in /etc/hyperglass and giving 777 full rights to the folder and files inside.
The same thing happens with the output plugins, please have a look. this is ssuch a great platform!
Configuration
web:
logo:
light: /etc/hyperglass/a.svg
dark: /etc/hyperglass/a.svg
favicon: /etc/hyperglass/a.svg
width: 40%
height: null
text:
title: Looking Glass
subtitle: provided by hyperglass
title_mode: all
Devices
devices:
- name: Oldenburg Test
address:
credential:
username:
password:
platform: juniper
directives:
- builtins: false
- juniper-bgp-route
- juniper-received-from-peer
attrs:
source4:
source6:
Logs
hyperglass-1 | [INFO] 20241030 14:26:30 |1762 | callHandlers → 80.228.255.62:52285 - "GET / HTTP/1.1" 200 {}
hyperglass-1 | [INFO] 20241030 14:26:31 |1762 | callHandlers → 80.228.255.62:52285 - "GET /_next/static/chunks/webpack-790cb4468e180930.js HTTP/1.1" 200 {}
hyperglass-1 | [INFO] 20241030 14:26:31 |1762 | callHandlers → 80.228.255.62:52286 - "GET /_next/static/nz0t2HFziAXTmh8bM1vdX/_buildManifest.js HTTP/1.1" 200 {}
hyperglass-1 | [INFO] 20241030 14:26:31 |1762 | callHandlers → 80.228.255.62:52287 - "GET /_next/static/nz0t2HFziAXTmh8bM1vdX/_ssgManifest.js HTTP/1.1" 200 {}
hyperglass-1 | [INFO] 20241030 14:26:31 |1762 | callHandlers → 80.228.255.62:52287 - "GET /_next/static/chunks/915.4213c6fa1c4191ba.js HTTP/1.1" 200 {}
hyperglass-1 | [DEBUG] 20241030 14:26:43 |111 | membership → Checking target membership {'target': '192.0.2.0/24', 'network': '0.0.0.0/0'}
hyperglass-1 | [DEBUG] 20241030 14:26:43 |116 | membership → Target membership verified {'target': '192.0.2.0/24', 'network': '0.0.0.0/0'}
hyperglass-1 | [DEBUG] 20241030 14:26:43 |123 | in_range → Target is in range {'target': '192.0.2.0/24', 'range': '0-32'}
hyperglass-1 | [DEBUG] 20241030 14:26:43 |107 | validate_query_target → Validation passed {'query': Query(query_location='oldenburg_test', query_target=['192.0.2.0/24'], query_type='juniper-bgp-route')}
hyperglass-1 | [INFO] 20241030 14:26:43 |79 | query → Starting query execution {'query': Query(query_location='oldenburg_test', query_target=['192.0.2.0/24'], query_type='juniper-bgp-route')}
hyperglass-1 | [DEBUG] 20241030 14:26:43 |97 | query → Cache miss {'query': Query(query_location='oldenburg_test', query_target=['192.0.2.0/24'], query_type='juniper-bgp-route'), 'cache_key': 'hyperglass.query.bd2cab8688de5e3c1830c75380ace8f313a2ef493f56a31e6afefc6840e5135f'}
hyperglass-1 | [DEBUG] 20241030 14:26:43 |51 | execute → {'query': Query(query_location='oldenburg_test', query_target=['192.0.2.0/24'], query_type='juniper-bgp-route'), 'device': 'oldenburg_test'}
hyperglass-1 | [DEBUG] 20241030 14:26:43 |46 | __init__ → Constructing query {'type': 'juniper-bgp-route', 'target': ['192.0.2.0/24']}
hyperglass-1 | [DEBUG] 20241030 14:26:43 |129 | queries → Constructed query {'type': 'juniper-bgp-route', 'target': ['192.0.2.0/24'], 'constructed_query': ['show route protocol bgp table inet.0 192.0.2.0/24 detail | display json']}
hyperglass-1 | [DEBUG] 20241030 14:26:43 |51 | collect → Connecting to device {'device': 'Oldenburg Test', 'address': 'None:None', 'proxy': None}
hyperglass-1 | [DEBUG] 20241030 14:26:52 |115 | query → Runtime: 8.6084 seconds {'query': Query(query_location='oldenburg_test', query_target=['192.0.2.0/24'], query_type='juniper-bgp-route')}
hyperglass-1 | [DEBUG] 20241030 14:26:52 |134 | query → Response cached {'query': Query(query_location='oldenburg_test', query_target=['192.0.2.0/24'], query_type='juniper-bgp-route'), 'cache_timeout': 120}
hyperglass-1 | [INFO] 20241030 14:26:52 |146 | query → Execution completed {'query': Query(query_location='oldenburg_test', query_target=['192.0.2.0/24'], query_type='juniper-bgp-route')}
hyperglass-1 | [INFO] 20241030 14:26:52 |1762 | callHandlers → 80.228.255.62:52292 - "POST /api/query HTTP/1.1" 201 {}
hyperglass-1 | [WARNING] 20241030 14:29:21 |1762 | callHandlers → Invalid HTTP request received. {}
hyperglass-1 | [INFO] 20241030 14:29:32 |1762 | callHandlers → 152.32.245.93:58616 - "GET / HTTP/1.1" 200 {}
hyperglass-1 | [INFO] 20241030 14:29:37 |1762 | callHandlers → 152.32.245.93:47778 - "GET /images/favicons/favicon-196x196.png HTTP/1.1" 200 {}
hyperglass-1 | [INFO] 20241030 14:29:38 |1762 | callHandlers → 152.32.245.93:47786 - "GET /robots.txt HTTP/1.1" 200 {}
hyperglass-1 | [INFO] 20241030 14:29:38 |1762 | callHandlers → 152.32.245.93:47798 - "GET /sitemap.xml HTTP/1.1" 404 {}
hyperglass-1 | [INFO] 20241030 14:29:38 |1762 | callHandlers → 152.32.245.93:47826 - "GET /_next/static/chunks/polyfills-c67a75d1b6f99dc8.js HTTP/1.1" 200 {}
hyperglass-1 | [INFO] 20241030 14:29:38 |1762 | callHandlers → 152.32.245.93:47824 - "GET /_next/static/nz0t2HFziAXTmh8bM1vdX/_buildManifest.js HTTP/1.1" 200 {}
hyperglass-1 | [INFO] 20241030 14:29:38 |1762 | callHandlers → 152.32.245.93:47802 - "GET /_next/static/chunks/webpack-790cb4468e180930.js HTTP/1.1" 200 {}
hyperglass-1 | [INFO] 20241030 14:29:38 |1762 | callHandlers → 152.32.245.93:47816 - "GET /_next/static/chunks/pages/_app-e5cf2d2230ac6396.js HTTP/1.1" 200 {}
hyperglass-1 | [INFO] 20241030 14:29:38 |1762 | callHandlers → 152.32.245.93:47840 - "GET /_next/static/chunks/pages/index-a1a889711ef3362f.js HTTP/1.1" 200 {}
hyperglass-1 | [INFO] 20241030 14:29:39 |1762 | callHandlers → 152.32.245.93:47866 - "GET /_next/static/chunks/main-113d9e0de0ebdeca.js HTTP/1.1" 200 {}
hyperglass-1 | [INFO] 20241030 14:29:39 |1762 | callHandlers → 152.32.245.93:47850 - "GET /_next/static/chunks/framework-f856061fb4a8c9e6.js HTTP/1.1" 200 {}
hyperglass-1 | [INFO] 20241030 14:29:39 |1762 | callHandlers → 152.32.245.93:47878 - "GET /_next/static/nz0t2HFziAXTmh8bM1vdX/_ssgManifest.js HTTP/1.1" 200 {}
hyperglass-1 | [INFO] 20241030 14:29:40 |1762 | callHandlers → 152.32.245.93:47892 - "GET /config.json HTTP/1.1" 404 {}
I have walked through the code and find out two possiblitiy location that might cause this bug.
at hyperglass/models/directive.py:317 validate_plugins function
316 @field_validator("plugins")
317 def validate_plugins(cls: "Directive", plugins: t.List[str]) -> t.List[str]:
318 """Validate and register configured plugins."""
319 plugin_dir = Settings.app_path / "plugins"
320
321 if plugin_dir.exists():
322 # Path objects whose file names match configured file names, should work
323 # whether or not file extension is specified.
324 matching_plugins = (
325 f
326 for f in plugin_dir.iterdir()
327 if f.name.split(".")[0] in (p.split(".")[0] for p in plugins)
328 )
329 return [str(f) for f in matching_plugins]
330 return []
- I think that () should be [], or else the results are always an empty array
- According to
plugin_dir, I think that currently it only reads from the file/etc/hyperglass/plugins. Instead of any location provided by the derivatives.
Therefore, the current solution is to
- Change () from line 324 to line 328 to []
- Put your plugin files in plugins directory which is
/etc/hyperglass/plugins
Thank you very much for your time. I changed directiy.py as to your solution to: 324 matching_plugins = [ 325 f 326 for f in plugin_dir.iterdir() 327 if f.name.split(".")[0] in (p.split(".")[0] for p in plugins) 328 ]
And put the plugins into the plugins directory but have the same results. I tried to put a print statement into the function in directive.py (without the arrows): def validate_plugins(cls: "Directive", plugins: t.List[str]) -> t.List[str]: """Validate and register configured plugins.""" plugin_dir = Settings.app_path / "plugins" --->print("PLUGIN_DIR: " + plugin_dir)<---------- if plugin_dir.exists(): # Path objects whose file names match configured file names, should work # whether or not file extension is specified. matching_plugins = [ f for f in plugin_dir.iterdir() if f.name.split(".")[0] in (p.split(".")[0] for p in plugins) ] return [str(f) for f in matching_plugins] return []
And the live output from the container doesnt show PLUGIN_DIR... should that be there or will a print in that function not work?
It seems that with docker, the code is actually run at /opt/hyperglass, /etc/hyperglass are only for setting files. My suggestion is to also mount the directory to /opt/hyperglass
e.i.
volumes:
- ${HYPERGLASS_APP_PATH-/etc/hyperglass}:/etc/hyperglass
- ${HYPERGLASS_APP_PATH-/etc/hyperglass}:/opt/hyperglass
if you have done correclty, it should print something out.
Thanks for the suggestion ! I changed the volumes as above and got the error:
hyperglass-1 | /usr/local/bin/python3: Error while finding module specification for 'hyperglass.console' (ModuleNotFoundError: No module named 'hyperglass')
hyperglass-1 exited with code 1
when starting the container. I changed
- ${HYPERGLASS_APP_PATH-/etc/hyperglass}:/opt/hyperglass
to |
- ${HYPERGLASS_APP_PATH-/opt/hyperglass}:/opt/hyperglass
and it works as expected, printing the output directly into the container output on the cli.
With that I could troubleshoot a bit and found a potential solution: In /opt/hyperglass/hyperglass/models/directive.py I added some prints to show the contents of plugins and plugin_dir.iterdir() beginning in line 316:
@field_validator("plugins")
def validate_plugins(cls: "Directive", plugins: t.List[str]) -> t.List[str]:
"""Validate and register configured plugins."""
plugin_dir = Settings.app_path / "plugins"
if plugin_dir.exists():
# Path objects whose file names match configured file names, should work
# whether or not file extension is specified.
----> for p in plugins:
print(f"ALLOWED PLUGIN: {p.split(".")[0]}")
for f in plugin_dir.iterdir():
print(f"FOUND PLUGIN: {f.name.split(".")[0]}")
matching_plugins = (
f
for f in plugin_dir.iterdir()
if f.name.split(".")[0] in (p.split(".")[0] for p in plugins)
)
return [str(f) for f in matching_plugins]
return []
and got the following result from the container:
hyperglass-1 | ALLOWED PLUGIN: /etc/hyperglass/plugins/transform_plugin
hyperglass-1 | FOUND PLUGIN: transform_plugin
hyperglass-1 | ALLOWED PLUGIN: /etc/hyperglass/plugins/transform_plugin
hyperglass-1 | FOUND PLUGIN: transform_plugin
To fix that problem I have following two solutions:
1: Swap lines 324-328 from
matching_plugins = (
f
for f in plugin_dir.iterdir()
if f.name.split(".")[0] in (p.split(".")[0] for p in plugins)
)
to
matching_plugins = []
for f in plugin_dir.iterdir():
for p in plugin_dir.iterdir():
if f.name.split(".")[0] == p.name.split(".")[0]:
matching_plugins.append(f)
2: Transform the strings in plugins.iterdir() to Path Objects and insert a .name. in the comparison of f and p:
plugins = [Path(p) for p in plugins]
matching_plugins = (
f
for f in plugin_dir.iterdir()
if f.name.split(".")[0] in (p.name.split(".")[0] for p in plugins)
)
But with approach 2. you have to also add
from pathlib import Path
at the beginning.
With these changes applied I am now getting:
hyperglass-1 | [DEBUG] 20241105 11:57:24 |116 | register → Registered built-in plugin {'type': 'output', 'name': 'BGPRoutePluginArista'}
hyperglass-1 | [DEBUG] 20241105 11:57:24 |116 | register → Registered built-in plugin {'type': 'output', 'name': 'BGPRoutePluginJuniper'}
hyperglass-1 | [DEBUG] 20241105 11:57:24 |116 | register → Registered built-in plugin {'type': 'output', 'name': 'MikrotikGarbageOutput'}
hyperglass-1 | [DEBUG] 20241105 11:57:24 |116 | register → Registered built-in plugin {'type': 'output', 'name': 'RemoveCommand'}
hyperglass-1 | [INFO] 20241105 11:57:24 |118 | register → Registered plugin {'type': 'input', 'name': 'TransformCIDR'}
What do you think about it, maybe theres a better solution.
Well, since I am not the author/maintainer of this project. I think that we should leave the answer to him; however he is disappeared from the Internet currently. Still, love to see that you have solve the problem. As for my opinion of the solutions
matching_plugins = (
f
for f in plugin_dir.iterdir()
if f.name.split(".")[0] in (p.name.split(".")[0] for Path(p) in plugins)
)
I think solution 2 with a slightly change might be a better solution, since it have the least change to the original code. Also I think that you might have some typo in solution 1?
matching_plugins = []
for f in plugin_dir.iterdir():
for p in plugins:
if f.name.split(".")[0] == p.split(".")[0]:
matching_plugins.append(f)
Not tested yet, but I think this is your original intention?
Thanks for your help ! Definitely the cleaner approach but the Path(p) throws errors on my instance. I kept
plugins = [Path(p) for p in plugins] matching_plugins = ( f for f in plugin_dir.iterdir() if f.name.split(".")[0] in (p.name.split(".")[0] for p in plugins) )
for further testing and discovered that my transform_input.py plugin is registered and executed. The line
target_network = ip_network(target)
throws errors as well but this is okay for me as I'm going to create own rules in the plugin.
I created a Output plugin juniper-bgp-route.py:
from hyperglass.plugins import OutputPlugin
print("OUTPUT PLUGIN CALLED")
class TestOutput(OutputPlugin):
def process(self, output, query):
print("FUNCTION CALLED")
return f"Plugin Test Successfull: {query}"
which gets registered in the container start but not executed after the query ( yes i bound that in another command in the directives.yaml). For further troubleshooting I did in /opt/hyperglass/hyperglass/plugins/_manager.py:
class OutputPluginManager(PluginManager[OutputPlugin], type="output"):
"""Manage Output Processing Plugins."""
def execute(self: "OutputPluginManager", *, output: OutputType, query: "Query") -> OutputType:
"""Execute all output parsing plugins.
The result of each plugin is passed to the next plugin.
"""
print(f"execute in OutputPluginManager Class in _manager.py called with query id: {query.directive.id} and platform: {query.device.platform}")
result = output
for plugin in self.plugins():
print(f"self plugin: {plugin}")
print(f"plugin directives: {plugin.directives}")
print(f"plugin platform: {plugin.platforms}")
directives = (
plugin
for plugin in self.plugins()
if query.directive.id in plugin.directives and query.device.platform in plugin.platforms
)
print(f"DIRECTIVES: {directives}")
common = (plugin for plugin in self.plugins() if plugin.common is True)
for plugin in (*directives, *common):
log.bind(plugin=plugin.name, value=result).debug("Output Plugin Starting Value")
result = plugin.process(output=result, query=query)
log.bind(plugin=plugin.name, value=result).debug("Output Plugin Ending Value")
if result is False:
return result
# Pass the result of each plugin to the next plugin.
return result
and did the query as before and now got the output:
hyperglass-1 | [DEBUG] 20241108 07:26:43 |51 | collect → Connecting to device {'device': 'Oldenburg Test', 'address': 'None:None', 'proxy': None}
hyperglass-1 | execute in OutputPluginManager Class in _manager.py called with query id: juniper-received-from-peer and platform: juniper
hyperglass-1 | self plugin: TestOutput
hyperglass-1 | plugin directives: ('juniper-received-from-peer',)
hyperglass-1 | plugin platform: ()
hyperglass-1 | self plugin: BGPRoutePluginArista
hyperglass-1 | plugin directives: ('hyperglass_arista_eos_bgp_route_table', 'hyperglass_arista_eos_bgp_aspath_table', 'hyperglass_arista_eos_bgp_community_table')
hyperglass-1 | plugin platform: ('arista_eos',)
hyperglass-1 | self plugin: BGPRoutePluginJuniper
hyperglass-1 | plugin directives: ('hyperglass_juniper_bgp_route_table', 'hyperglass_juniper_bgp_aspath_table', 'hyperglass_juniper_bgp_community_table')
hyperglass-1 | plugin platform: ('juniper',)
hyperglass-1 | self plugin: MikrotikGarbageOutput
hyperglass-1 | plugin directives: ('hyperglass_mikrotik_bgp_aspath', 'hyperglass_mikrotik_bgp_community', 'hyperglass_mikrotik_bgp_route', 'hyperglass_mikrotik_ping', 'hyperglass_mikrotik_traceroute')
hyperglass-1 | plugin platform: ('mikrotik_routeros', 'mikrotik_switchos')
hyperglass-1 | self plugin: RemoveCommand
hyperglass-1 | plugin directives: ()
hyperglass-1 | plugin platform: ()
hyperglass-1 | DIRECTIVES: <generator object OutputPluginManager.execute.
so it looks like there's no platform set for the plugin and therefore the plugin fails the check in directives = ( plugin for plugin in self.plugins() if query.directive.id in plugin.directives and query.device.platform in plugin.platforms )
based on the builtin plugins i added the marked line to my plugin juniper-bgp-route.py:
import re, json
from hyperglass.plugins import OutputPlugin
------> from typing import List, Sequence <-----------
print("OUTPUT PLUGIN CALLED")
class TestOutput(OutputPlugin):
-------> platforms: Sequence[str] = ("juniper",) <----------------
def process(self, output, query):
print("FUNCTION CALLED")
return f"Plugin Test Successfull: {query}"
Now the output plugin works as expected and the query output is Plugin Test Successfull...
You mentioned that the creator is currently not responding on this project therefore I'm thinking about forking my changes, so that others with the same problems have a solution until the creator fixes these problems in this original repository .
With that in mind I would also remove the platform check in the /opt/hyperglass/hyperglass/plugins/_manager.py from
directives = (
plugin
for plugin in self.plugins()
if query.directive.id in plugin.directives and query.device.platform in plugin.platforms
)
to
directives = (
plugin
for plugin in self.plugins()
if query.directive.id in plugin.directives
)
I am also thinking of forking one and fix other issues. Perhaps create a hyperglass 2 or something. Do U mind inviting U to create a PR after the new hyperglass is created?
Sure would be glad, after all your help.
Good afternoon, gentlemen. I appreciate your notes about the "possible bug" @WalkerD243 and @N0Ball.
I made a fork and reviewed the code, and I noticed something: the config.yml file lacks information about the plugins we want to load. Here's an example:
- I loaded a plugin in
/etc/hyperclass/plugins/redact_bgp.py:
import re
from hyperglass.plugins import OutputPlugin
EXTENDED_COMMUNITY_PATTERN = re.compile(r"^\s*Extended Community:.*$", re.MULTILINE)
class RemoveExtendedCommunity(OutputPlugin):
def process(self, output, query):
result = []
for each_output in output:
# Remove as linhas que correspondem ao padrão
cleaned_output = EXTENDED_COMMUNITY_PATTERN.sub("", each_output)
result.append(cleaned_output.strip())
return result
This plugin removes the "Extended Community" line from the FRRouting return, which I am using.
- In
/etc/hyperglass/directives.yml, I registered a directive to log the plugin:
frr-bgp-route-redact:
name: Rotas BGP
plugins:
- "/etc/hyperglass/plugins/Redact.py" <--------------
rules:
- condition: ".*:.*" # Identifica endereços IPv6
command: "vtysh -c 'show bgp ipv6 unicast {target}'"
- condition: "\\d+\\.\\d+\\.\\d+\\.\\d+.*" # Identifica endereços IPv4
command: "vtysh -c 'show bgp ipv4 unicast {target}'"
field:
description: "Prefixo"
OBS.: field is required
- In
/etc/hyperglass/devices.yml, I registered the directives in my device:
devices:
...
directives:
- builtins: false
- frr-bgp-route-redact
- In
/etc/hyperglass/config.yml, I activated the plugin like this:
plugins:
- redact_bgp.py
What was missing in my configurations was step 4, where I needed to specify the plugin I wanted to activate.
- Restart hyperglass
It was not necessary to modify the code mentioned by you. I am using ChatGPT to translate from Portuguese (Pt-BR) to English.
@Flugelo yeah you're right ! I tested that with a fresh install and did as you said in Step 4. Is Step 5. missing ?
However a major problem is still that all plugins (Input and Ouput) specified in different directives get applied to all directives .
@WalkerD243, Step 6 is meant to be step 5, sorry for the typo.
Regarding: 'all plugins (input and output) specified in different directives are applied to all directives,' I didn't encounter this issue; each directive has its own function without affecting the others.
Hey! I've encountered the same problem with the plugins not working, also using container. Here's how I managed to make it work:
Your plugins files should be visible from inside the container in the folder HYPERGLASS_APP_PATH/plugins
For instance my HYPERGLASS_APP_PATH variable is set to /etc/hyperglass/, then the plugins folder is at /etc/hyperglass/plugins, so create the volume mappings acordingly.
Next in the config.yaml you need to create the plugins attribute with the name of the plugin file, instead of the path to the plugin.
Also use the file name of the plugin in the directives file.
config.yaml
...
# Tells hyperglass to load theese plugins
plugins: ["transform_input.py"]
...
directives.yaml
...
bgp-route-sanitized:
name: BGP Route
# Tells hyperglass to use this plugin on the directive
plugins:
- "transform_input.py"
rules:
- condition: "0.0.0.0/0"
command: "display bgp routing-table {target} | no-more"
- condition: "::/0"
command: "display bgp ipv6 routing-table {target} | no-more"
field:
description: "IP Address, Prefix, or Hostname"
validation: '[0-9a-f\.\:]+(\/\d+)?'
...
Folder structure on host (not as how the container sees, for the container this folder is mapped into /etc/hyperglass)
hyperglass@hyperglass ~/container> tree plugins/
plugins/
└── transform_input.py
1 directory, 1 file
@WalkerD243 @Flugelo @N0Ball Would you all consider this issue resolved with @JelsonRodrigues suggestions?