gabstopper / ansible-stonesoft

Ansible libraries for automating Stonesoft FW Management
Apache License 2.0
10 stars 3 forks source link

generic_element task syntax #33

Open sebbbastien opened 5 years ago

sebbbastien commented 5 years ago

Hi David,

Not sure if I have to open a new case or respond to #30, but the question is different, so I choose to open a new issue.

Until now I create Multilink using smc-python, but generic_element could allow me to save some time on idempotency work. I manage to use generic_element to create some simple elements, but I'm stuck on Multilink element.

generic_element library use Multilink.update_or_create(), and this function use multilink_members. Next argument should be a list of multilink_member, each build from name, netlink_role and eventualy ip_range. I tried a lot of syntax, but none was working.

Is-it possible to deduct the attended structure from smc-python output (from element.data.data for example)?

Regards,

gabstopper commented 5 years ago

Hi Sebbastien, Yes, the easiest way to inspect an element is to create in SMC, then pull down with smc-python, like this. I have this multilink: image

Then pull down and inspect:

a = Multilink('outbound-sdwan')
pprint(vars(a.data)) 

results in:

{'_etag': '"MTU4ODY5MjQxNjcxNTA5MDQxMzI2NDE2"',
 'data': {u'etag': u'MTU4ODY5MjQxNjcxNTA5MDQxMzI2NDE2',
          u'key': 158869,
          u'link': [{u'href': u'http://172.18.1.150:8082/6.5/elements/outbound_multilink/158869',
                     u'rel': u'self',
                     u'type': u'outbound_multilink'},
                    {u'href': u'http://172.18.1.150:8082/6.5/elements/outbound_multilink/158869/export',
                     u'rel': u'export'},
                    {u'href': u'http://172.18.1.150:8082/6.5/elements/outbound_multilink/158869/history',
                     u'rel': u'history'},
                    {u'href': u'http://172.18.1.150:8082/6.5/elements/outbound_multilink/158869/search_category_tags_from_element',
                     u'rel': u'search_category_tags_from_element'},
                    {u'href': u'http://172.18.1.150:8082/6.5/elements/outbound_multilink/158869/duplicate',
                     u'rel': u'duplicate'}],
          u'multilink_member': [{u'ip_range': u'172.18.1.241',
                                 u'netlink_ref': u'http://172.18.1.150:8082/6.5/elements/netlink/164554',
                                 u'netlink_role': u'active',
                                 u'network_ref': u'http://172.18.1.150:8082/6.5/elements/network/979',
                                 u'qos_role': [{u'netlink_role': u'active',
                                                u'qos_class_ref': u'http://172.18.1.150:8082/6.5/elements/qos_class/7'}]},
                                {u'ip_range': u'10.0.0.254',
                                 u'netlink_ref': u'http://172.18.1.150:8082/6.5/elements/netlink/164555',
                                 u'netlink_role': u'active',
                                 u'network_ref': u'http://172.18.1.150:8082/6.5/elements/network/120',
                                 u'qos_role': [{u'netlink_role': u'active',
                                                u'qos_class_ref': u'http://172.18.1.150:8082/6.5/elements/qos_class/2'},
                                               {u'netlink_role': u'standby',
                                                u'qos_class_ref': u'http://172.18.1.150:8082/6.5/elements/qos_class/7'}]}],
          u'multilink_method': u'rtt',
          u'multilink_qos_class': [{u'balance_method': u'rtt',
                                    u'qos_class_ref': u'http://172.18.1.150:8082/6.5/elements/qos_class/2'},
                                   {u'balance_method': u'rtt',
                                    u'qos_class_ref': u'http://172.18.1.150:8082/6.5/elements/qos_class/7'}],
          u'name': u'outbound-sdwan',
          u'read_only': False,
          u'retries': 2,
          u'system': False,
          u'timeout': 3600}}

Multilink members are somewhat complex because they require resolution to other element references that must already exist in SMC. Although generic element will handle that properly as long as you use the same structure.

sebbbastien commented 5 years ago

Hi David,

I've "patched" the Multilink.update_or_create() method to understand (added two print() lines):

    @classmethod
    def update_or_create(cls, with_status=False, **kwargs):
        element, updated, created = super(Multilink, cls).update_or_create(
            with_status=True, defer_update=True, **kwargs)
        if not created:
            multilink_members = kwargs.pop('multilink_members', [])
            if multilink_members:
+               print(multilink_members)
+               print(element.members)
                if set(multilink_members) ^ set(element.members):
                    element.data['multilink_member'] = multilink_members    
                    updated = True
        if updated:
            element.update()
        if with_status:
            return element, updated, created
        return element

Using smc-python I get:

In [62]: Multilink.update_or_create(name='OML_Test', multilink_members=[MultilinkMember.create(DynamicNetlink(name='dynamic_netlink-C0001-(IPv4)-Interface 0 (3)'))])                                                                          
[MultilinkMember(netlink=DynamicNetlink(name=dynamic_netlink-C0001-(IPv4)-Interface 0 (3)),netlink_role=active,ip_range=0.0.0.0)]
[MultilinkMember(netlink=StaticNetlink(name=NL_TEST),netlink_role=active,ip_range=1.2.3.3-1.2.3.4)]
Out[62]: Multilink(name=OML_Test)

multilink_members is xor (^) with element.members, both are lists of MultilinkMember objects.

Using this playbook:

- name: Create a network element
  hosts: localhost
  gather_facts: no
  tasks:
    - name: Example network element creation
      generic_element:
        elements:

          - outbound_multilink:
              name: OML_Test
              multilink_members:
                - multilink_member:
                    staticnetlink:
                      name: "NL_TEST"
                    netlink_role: "standby"
                    ip_range: "1.2.3.1-1.2.3.2"

the error message is:

fatal: [localhost]: FAILED! => {
    "changed": false,
    "module_stderr": "<stdin>:17: DeprecationWarning: the imp module is deprecated in favour of importlib; see the module's documentation for alternative uses\nTraceback (most recent call last):\n  File \"<stdin>\", line 113, in <module>\n  File \"<stdin>\", line 105, in _ansiballz_main\n  File \"<stdin>\", line 48, in invoke_module\n  File \"/usr/lib/python3.7/imp.py\", line 234, in load_module\n    return load_source(name, filename, file)\n  File \"/usr/lib/python3.7/imp.py\", line 169, in load_source\n    module = _exec(spec, sys.modules[name])\n  File \"<frozen importlib._bootstrap>\", line 630, in _exec\n  File \"<frozen importlib._bootstrap_external>\", line 728, in exec_module\n  File \"<frozen importlib._bootstrap>\", line 219, in _call_with_frames_removed\n  File \"/tmp/ansible_generic_element_payload_wx5v3j4l/__main__.py\", line 186, in <module>\n  File \"/tmp/ansible_generic_element_payload_wx5v3j4l/__main__.py\", line 183, in main\n  File \"/tmp/ansible_generic_element_payload_wx5v3j4l/__main__.py\", line 131, in __init__\n  File \"/tmp/ansible_generic_element_payload_wx5v3j4l/ansible_generic_element_payload.zip/ansible/module_utils/stonesoft_util.py\", line 487, in __init__\n  File \"/tmp/ansible_generic_element_payload_wx5v3j4l/__main__.py\", line 158, in exec_module\n  File \"/home/seb/dev/ansible-stonesoft-sbt/env/lib/python3.7/site-packages/smc/elements/netlink.py\", line 398, in update_or_create\n    if set(multilink_members) ^ set(element.members):\nTypeError: unhashable type: 'dict'\n",
    "module_stdout": "[{'multilink_member': {'staticnetlink': {'name': 'NL_TEST'}, 'netlink_role': 'standby', 'ip_range': '1.2.3.1-1.2.3.2'}}]\n[MultilinkMember(netlink=DynamicNetlink(name=dynamic_netlink-C0001-(IPv4)-Interface 0 (3)),netlink_role=active,ip_range=0.0.0.0)]\n",
    "msg": "MODULE FAILURE\nSee stdout/stderr for the exact error",
    "rc": 1
}

Module stdout is:

Here, the second print still show a list of MultilinkMember, but first argument is "raw" json.

Although generic element will handle that properly as long as you use the same structure.

I don't understand how generic_element can handle [Static|Dynamic]Netlink when creating a MultilinkMember.