How to extend Coin
==================

Coin can be extended by writing your own backends, so that you can support
various access technologies by using the tools you want.

The common part of Coin already handles the administrative details of
subscriptions: members, subscriptions cost and billing strategy,
generating bills, etc.

It also handles the IP allocation process.  The administrator can define
pools of IP addresses, in which subnets are automatically allocated to
subscribers.  Each subscription can be attached any number of IPv4 or IPv6
subnets, which somehow get routed to the subscriber (the actual way of doing
this depends on the backend).  Management of reverse DNS is an upcoming
feature.

All the rest is up to you, that is, the technical side of subscriptions.
You will probably want to handle:

- authentication (e.g. Radius login and password, TLS certificates...)

- accounting (e.g. collecting and displaying graphs of user traffic)

- routing (e.g. inserting static routes, or signalling a routing daemon
  about new routes)

- technology-specific information (e.g. phone number associated to a DSL
  line, MAC address of a CPE, GPS coordinates for wireless subscribers)

- stuff we didn't think about when writing this


This can be done in three steps:

- write a Django application, whose models describe the data you need

- optionally, implement views for presenting some of this information to users

- write a backend to distribute needed configuration data to the rest of
  the infrastructure (routers, switches, log servers, accounting backend...)

How you implement the actual backend is completely up to you.  It can be a
SQL database (useful for Radius), a LDAP database, simply inserting static
routes in the kernel (if Coin runs on one of your routers, which is
probably not a good idea), writing configuration to text files, relying on
an orchestration tool such as Puppet or Ansible, etc.


Simple example: no view, no backend
-----------------------------------

A very simple application is provided with Coin: it's called `simple_dsl`.

This application provides a simple model for DSL subscribers (just a phone
number, no authentication), and doesn't use any backend.  It is intended
more as a demonstration, but it is perfectly usable, and should fulfil the
needs of small ISPs selling "white label" DSL lines.

It is probably a good starting point for writing your own application.  If
you need more features, read on.

More complex example with views and a LDAP backend
--------------------------------------------------

See `coin/vpn` for a much more complex application: OpenVPN access with
login/password and an arbitrary number of subnets routed to the user.  The
user has an interface for generating a password and for choosing which IP
addresses it wants to use.  All this configuration data is pushed to a
LDAP database, which is then used by the OpenVPN server.  OpenVPN
interfaces with LDAP both natively (for authenticating users) and through
shell scripts (for routes and IP addresses).


Complete specification
======================

Models
------

Your model must inherit from `coin.configuration.models.Configuration`.
This way, it will be automatically integrated in the generic admin
interface, and will gain the ability to be associated to IP subnets.

If you define a Meta class with a `verbose_name` attribute, it will be
used to identify your configuration backend in the interface (otherwise
the name of the class will be used).

If you want to provide views for your model, you must define an
`url_namespace` attribute, which is a string defining the URL namespace
associated to your view.  By default, the (lowercased) name of the class
will be used.

You should also define a `subnet_event(self)` method, which will be called
whenever the IP subnets associated to a configuration object have changed
(new subnet, deleted subnet, modified subnet).  You can use the
`ip_subnet` related name to have access to all IP subnets associated to
the object (for instance, `self.ip_subnet.all()` will give you a list of
`coin.resources.models.IPSubnet` objects).

Note that, like all Django models, you should define a `__unicode__`
method to describe an object of your class.


Admin
-----

Your admin model must inherit from
`coin.configuration.admin.ConfigurationAdminFormMixin` and
`polymorphic.admin.PolymorphicChildModelAdmin` (in this order).
Otherwise, it's a perfectly regular admin model (see `simple_dsl`), except
for the specificities described below.

You must define a `inline` attribute, set to an inline admin model for
your model (for instance, built on `admin.StackedInline` or
`admin.TabularInline`; again, see `simple_dsl`).  This inline model will
be used in the generic admin interface, so that administrators can edit
the backend details directly from a subscription object.

If you don't have any view, remember to set the `view_on_site` attribute
to `False`, so that Django's admin will not show a "View on site" button.


Views
-----

If you want to provide views for your model, you must provide at least a
"details" view, which will be used to display information about your
configuration objects to end-users.  For instance, you can inherit from
`django.views.generic.detail.DetailView`, or
`django.views.generic.edit.UpdateView` if you want users to edit some of
the fields (see `coin/vpn/views.py`).

Here is an example URL pattern to be used in your `urls.py`:

    url(r'^(?P<id>\d+)$', VPNView.as_view(template_name="vpn/vpn.html"), name="details")

Note that this pattern **must** be called "details".
Of course, you can add as many additional views as you want.

URLs
----

App views URLs are pluggable, you only have to tell your app to declare its
URLs. Then its URLs will be available under `<app_name>/<view_name>` (as long s
your app is listed in `INSTALLED_APPS`).

To do so :

1. Create a `<app_name>/apps.py` like (important part is inheriting
   `coin.apps.AppURLs`) :

    from django.apps import AppConfig
    import coin.apps

    class MyAppConfig(AppConfig, coin.apps.AppURLs):
        name = 'myapp'
        verbose_name = "Fruity app !"

2. Edit a `<app_dir>/__init__.py` :

    default_app_config = 'coin.myapp.apps.MyAppConfig


Optionaly, you can customize which URLs are plugged and to which prefix via the
`exported_urlpatterns` var on your config class as a list of
`<prefix>,<urlpatterns>` :

        class MyAppConfig(AppConfig, coin.apps.AppURLS):
            name = 'my_app'
            exported_urlpatterns = [('coolapp', 'my_app.cool_urls')]