using device context with static ip definition

27 views
Skip to first unread message

João Henrique Albuquerque

unread,
Feb 5, 2020, 1:33:44 PM2/5/20
to OpenWISP
Hi I'd like to know if is possible to use and device context to automactic increment my devices IP address for local area networks (LAN) when device subscribes to the controller.

I would like to acheive the following:

1) Device subscribes to the controller and get the config from templates (lan is defined with a generic static address);

2) Device Context ip field is updated automatically based on last value used (update ip filed on a interface);

3) Controller sends the AP configuration with context updated IP address ;

I've seen this is a rather old discussion, but couldn't find anything in the docs that would help to achieve this without manually update the device context for each device.

Any suggestions?

Federico Capoano

unread,
Feb 5, 2020, 2:15:26 PM2/5/20
to OpenWISP
There's no out of the box solution to this yet.

The simplest custom solution I can think of is to hook a signal on device creation which fills the context field accordingly.
An even better solution could be to add openwisp-ipam to the instance, creating an ip each time and getting the first available IP using openwisp-ipam, although there's no integration with openwisp-controller yet.

I hope this helps.

Federico

--
You received this message because you are subscribed to the Google Groups "OpenWISP" group.
To unsubscribe from this group and stop receiving emails from it, send an email to openwisp+u...@googlegroups.com.
To view this discussion on the web, visit https://groups.google.com/d/msgid/openwisp/7c95a5ba-d106-4df1-abed-d6a1228ad887%40googlegroups.com.

João Henrique Albuquerque

unread,
Feb 5, 2020, 3:15:57 PM2/5/20
to open...@googlegroups.com
Thanks for the suggestions,

Is there any documentation that you could help me get started with openwisp-ipam?

About the signal on device creation, I should probably do it under the DB I'm using, right?
E.g, when a new entry is made to the config_device tables update the field with context straight to the backend of the application.




Oliver Kraitschy

unread,
Feb 5, 2020, 3:39:04 PM2/5/20
to open...@googlegroups.com
On Wed, Feb 05, 2020 at 05:15:43PM -0300, João Henrique Albuquerque wrote:
>
> About the signal on device creation, I should probably do it under the DB
> I'm using, right?
> E.g, when a new entry is made to the config_device tables update the field
> with context straight to the backend of the application.

https://docs.djangoproject.com/en/3.0/ref/signals/#post-save

That signal is emitted if an instance of a model class has been saved. You
can then check if it was an instance of the Device model class and if yes,
determine the ip to use and fill the device context accordingly.

Greetings,
Oliver

João Henrique Albuquerque

unread,
Feb 5, 2020, 4:23:55 PM2/5/20
to open...@googlegroups.com
Thanks for the pointers, Oliver.

I'll look into it later

--
You received this message because you are subscribed to the Google Groups "OpenWISP" group.
To unsubscribe from this group and stop receiving emails from it, send an email to openwisp+u...@googlegroups.com.

Federico Capoano

unread,
Feb 6, 2020, 3:27:30 AM2/6/20
to OpenWISP
There's no documentation on how to accomplish that yet, but we will help you out.
If you do accomplish it and will want to share your solution I believe it will be beneficial to the community.

As first thing, I suggest you to implement this in development mode (not on your instance).
Creating a new django app in which you add a new model, eg: DeviceIp, in which you add a ForeignKey field pointing to Device and a ForeignKey pointing to Device, the two fields should be flagged as unique_together (look all these terms on the django documentation).

Then add a signal receiver function in this new app which listens to save events on Device, return if the "created" parameter is False so that you act only on created devices, and in this function you add the logic to write to the context field of the Device. There should be a method in django-ipam to get the first available ip from a subnet.

Then you should decide also how to manage subnets, if all devices are on the same subnet is easy, otherwise you should probably have a way to tell the system to which subnet the device pertains to. 

Thanks
Federico

João Henrique Albuquerque

unread,
Feb 6, 2020, 8:37:46 AM2/6/20
to OpenWISP
Just for clarifcation did you mean two ForeignKey pointing to the Device ?
Shouldn't one be pointing to device config or device context?

Federico Capoano

unread,
Feb 6, 2020, 3:21:19 PM2/6/20
to OpenWISP
Sorry, I meant one ForeignKey to the Device model of openwisp-controller and one to the Ip model of openwisp-ipam.
You may as well create an admin inline and add it to the DeviceAdmin dynamically (it's called monkey patching) so you'll be able to see this in the django admin.

Here's an example of how to listen for signals in a django project and openwisp.

from django.apps import AppConfig
from django.db.models.signals import post_save

class ExampleApp(AppConfig):
    name
= 'openwisp2.example_app'
    label
= 'example_app'


   
def ready(self):
       
from openwisp_controller.config.models import Device


        post_save
.connect(self.device_saved_receiver,
                          sender
=Device,
                          dispatch_uid
='device_saved_receiver')


   
@classmethod
   
def device_saved_receiver(cls, sender, instance, created, **kwargs):
       
# if not created:
       
#     False
       
# do something
       
pass

    @classmethod
   
def device_deleted_receiver(cls, sender, instance, **kwargs):
       
# may want to do something on delete
       
pass


There's different way of doing this depending on how you want to do it.
If you do create a custom django app mixing openwisp-controller and openwisp-ipam you may upload it on github and we'll help you out there as well.

Alternatively, you can look for a simpler solution, in that case, you just need to place that file in the openwisp project folder (eg /opt/openwisp2/openwisp2 when using ansible-openwisp2) and then add to the INSTALLED_APPS the following: "openwisp2.example_app.ExampleApp".

Federico

João Henrique Albuquerque

unread,
Mar 3, 2020, 5:00:50 PM3/3/20
to open...@googlegroups.com
Hi, just a little follow up on this topic, I've set up a git to work on a custom app called DeviceIP.

you can check my progress here

  1. I've added the openwisp_ipam and my DeviceIP app to the openwisp-controller project.
  2. Added some comments on what I'm planning to do with my signals.py (to help with the logic setup)
  3. Added some code on models.py to track devices and IP addresses.
I'll try to work on the logic for the signals implementation but I believe the comments I've made should cover all the cases.

Let me know what you guys think it could be improved or suggestions for the code implementation

Cheers,


--
You received this message because you are subscribed to the Google Groups "OpenWISP" group.
To unsubscribe from this group and stop receiving emails from it, send an email to openwisp+u...@googlegroups.com.

João Henrique Albuquerque

unread,
Mar 30, 2020, 2:06:41 PM3/30/20
to OpenWISP
I've made some more progress in a solution for my initial issue using openwisp_ipam. I've managed to request an IP address and save the resulting ip in the subnet with my signal function, now I need to assign to the static interface config of the device I'm trying to save.

I've defined the JSON with the network interface that I want to use, next step is set column config of the config_config table that has the device id, I'm not so sure how to access the confg_config with the foreign key pointing to my device object, should I import another model?

 I noticed that when I'm manually adding the device from the Django admin interface I need to create the device config manually, but devices subscribing to the controller create this entry automatically.

Still need some improvements but the code looks like this:


 if created:
       #get device_id and device_org_id in device object
       device = Device.objects.first()
       device_id = str(getattr(device, 'id'))
       device_org_id = str(getattr(device, 'organization_id'))
       #define a config object
       config = Config.objects.filter(device_id=device_id).first
       # to be use later to filer subnets inside same orgs
       #get org subnet_id
       r = requests.get('http://127.0.0.1:8000/api/v1/subnet/',auth=('user','pass')).json()
       sub_id= str(r["results"][0]['id'])
       mask = str(r["results"][0]['subnet'].split('/',3)[1])
       req_ip = requests.get('http://127.0.0.1:8000/api/v1/subnet/'+str(sub_id)+'/get-first-available-ip/',auth=('user','pass')).json()
       p_data = { 'description': 'auto generated ip', 'ip_address': temp_ip, 'subnet': sub_id }
        #save ip to device's subnet config
       r = requests.post('http://127.0.0.1:8000/api/v1/subnet/'+str(sub_id)+'/request-ip/', json=p_data, auth=('user','pass'))
       #config to be inserted in the database
        config_list = {'interfaces': [{'type': 'bridge', 'bridge_members': ['eth0.1', 'eth0.2'], 'name': 'cable', 'disabled': False, 'addresses': [{'proto': 'static', 'family': 'ipv4', 'address': req_ip, 'mask': mask}], 'network': 'cable'}]}
       #insert json in config column of config_device table
       setattr(config, 'config',json.dumps(config_list))
       #get device config_id

Thanks for the suggestions so far. I'm open to feedback how to improve this .

Cheers

Federico Capoano

unread,
Mar 30, 2020, 11:54:31 PM3/30/20
to OpenWISP
Hey João,

On Mon, Mar 30, 2020 at 1:06 PM João Henrique Albuquerque <joaoh...@gmail.com> wrote:
I've made some more progress in a solution for my initial issue using openwisp_ipam. I've managed to request an IP address and save the resulting ip in the subnet with my signal function, now I need to assign to the static interface config of the device I'm trying to save.
 
Great to see you're moving on.

I've defined the JSON with the network interface that I want to use, next step is set column config of the config_config table that has the device id, I'm not so sure how to access the confg_config with the foreign key pointing to my device object, should I import another model?
 
I did not understand this passage

I noticed that when I'm manually adding the device from the Django admin interface I need to create the device config manually, but devices subscribing to the controller create this entry automatically.

Still need some improvements but the code looks like this:


 if created:
       #get device_id and device_org_id in device object
       device = Device.objects.first()

The receiver function you connect to the signal, will receive the instance of the device being created, so you don't need to query the DB again.
Moreover, calling first() may not yield the desired result.
 
       device_id = str(getattr(device, 'id'))
       device_org_id = str(getattr(device, 'organization_id'))
       #define a config object
       config = Config.objects.filter(device_id=device_id).first

Try device.config to access the configuration.

try:
    device.config
except ObjectDoesNotExist:
    # you may create the config object here if it does not exist
    # but I'm not sure what happens if you also create the device manually
 
       # to be use later to filer subnets inside same orgs
       #get org subnet_id
       r = requests.get('http://127.0.0.1:8000/api/v1/subnet/',auth=('user','pass')).json()
       sub_id= str(r["results"][0]['id'])
       mask = str(r["results"][0]['subnet'].split('/',3)[1])

You can use the django models to get this information instead of using the API, it would be a lot faster.
 
       req_ip = requests.get('http://127.0.0.1:8000/api/v1/subnet/'+str(sub_id)+'/get-first-available-ip/',auth=('user','pass')).json()

You can use the request_ip method on the Subnet model to do this: https://github.com/openwisp/django-ipam/blob/master/django_ipam/base/models.py#L74-L85

The API is meant to be used on third-party applications. In this case instead you have direct access to the python/django API of OpenWISP.
 
       p_data = { 'description': 'auto generated ip', 'ip_address': temp_ip, 'subnet': sub_id }
        #save ip to device's subnet config
       r = requests.post('http://127.0.0.1:8000/api/v1/subnet/'+str(sub_id)+'/request-ip/', json=p_data, auth=('user','pass'))

You can do directly request_ip and avoid get_first_available_ip.
 
       #config to be inserted in the database
        config_list = {'interfaces': [{'type': 'bridge', 'bridge_members': ['eth0.1', 'eth0.2'], 'name': 'cable', 'disabled': False, 'addresses': [{'proto': 'static', 'family': 'ipv4', 'address': req_ip, 'mask': mask}], 'network': 'cable'}]}
       #insert json in config column of config_device table
       setattr(config, 'config',json.dumps(config_list))

I believe you can just do:

config.config = config_list

config_list should be renamed to config_dict, since it's a dictionary and not a list.
Reply all
Reply to author
Forward
0 new messages