network.py 6.71 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
# Copyright 2014 Budapest University of Technology and Economics (BME IK)
#
# This file is part of CIRCLE Cloud.
#
# CIRCLE is free software: you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option)
# any later version.
#
# CIRCLE is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along
# with CIRCLE.  If not, see <http://www.gnu.org/licenses/>.

18
from __future__ import absolute_import, unicode_literals
Őry Máté committed
19 20 21 22 23

from logging import getLogger
from netaddr import EUI, mac_unix

from django.db.models import Model, ForeignKey, BooleanField
24
from django.utils.translation import ugettext_lazy as _, ugettext_noop
Őry Máté committed
25

26
from common.models import create_readable
Őry Máté committed
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
from firewall.models import Vlan, Host
from ..tasks import net_tasks

logger = getLogger(__name__)


class InterfaceTemplate(Model):

    """Network interface template for an instance template.

    If the interface is managed, a host will be created for it.
    """
    vlan = ForeignKey(Vlan, verbose_name=_('vlan'),
                      help_text=_('Network the interface belongs to.'))
    managed = BooleanField(verbose_name=_('managed'), default=True,
                           help_text=_('If a firewall host (i.e. IP address '
                                       'association) should be generated.'))
    template = ForeignKey('InstanceTemplate', verbose_name=_('template'),
                          related_name='interface_set',
                          help_text=_('Template the interface '
                                      'template belongs to.'))

    class Meta:
        app_label = 'vm'
        db_table = 'vm_interfacetemplate'
        permissions = ()
        verbose_name = _('interface template')
        verbose_name_plural = _('interface templates')

56 57 58
    def __unicode__(self):
        return "%s - %s - %s" % (self.template, self.vlan, self.managed)

Őry Máté committed
59 60 61 62 63 64 65 66 67 68 69 70 71 72

class Interface(Model):

    """Network interface for an instance.
    """
    vlan = ForeignKey(Vlan, verbose_name=_('vlan'),
                      related_name="vm_interface")
    host = ForeignKey(Host, verbose_name=_('host'),  blank=True, null=True)
    instance = ForeignKey('Instance', verbose_name=_('instance'),
                          related_name='interface_set')

    class Meta:
        app_label = 'vm'
        db_table = 'vm_interface'
73
        ordering = ("-vlan__managed", )
Őry Máté committed
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91

    def __unicode__(self):
        return 'cloud-' + str(self.instance.id) + '-' + str(self.vlan.vid)

    @property
    def mac(self):
        try:
            return self.host.mac
        except:
            return Interface.generate_mac(self.instance, self.vlan)

    @classmethod
    def generate_mac(cls, instance, vlan):
        """Generate MAC address for a VM instance on a VLAN.
        """
        # MAC 02:XX:XX:XX:XX:XX
        #        \________/\__/
        #           VM ID   VLAN ID
92 93
        class mac_custom(mac_unix):
            word_fmt = '%.2X'
Őry Máté committed
94 95 96
        i = instance.id & 0xfffffff
        v = vlan.vid & 0xfff
        m = (0x02 << 40) | (i << 12) | v
97
        return EUI(m, dialect=mac_custom)
Őry Máté committed
98 99 100 101 102 103 104 105 106 107 108 109 110

    def get_vmnetwork_desc(self):
        return {
            'name': self.__unicode__(),
            'bridge': 'cloud',
            'mac': str(self.mac),
            'ipv4': str(self.host.ipv4) if self.host is not None else None,
            'ipv6': str(self.host.ipv6) if self.host is not None else None,
            'vlan': self.vlan.vid,
            'managed': self.host is not None
        }

    @classmethod
111
    def create(cls, instance, vlan, managed, owner=None, base_activity=None):
Őry Máté committed
112 113 114 115 116 117 118 119
        """Create a new interface for a VM instance to the specified VLAN.
        """
        if managed:
            host = Host()
            host.vlan = vlan
            # TODO change Host's mac field's type to EUI in firewall
            host.mac = str(cls.generate_mac(instance, vlan))
            host.hostname = instance.vm_name
120
            # Get addresses from firewall
121
            if base_activity is None:
122
                act_ctx = instance.activity(
123 124
                    code_suffix='allocating_ip',
                    readable_name=ugettext_noop("allocate IP address"),
125
                    user=owner)
126
            else:
127 128 129
                act_ctx = base_activity.sub_activity(
                    'allocating_ip',
                    readable_name=ugettext_noop("allocate IP address"))
130
            with act_ctx as act:
131 132 133
                addresses = vlan.get_new_address()
                host.ipv4 = addresses['ipv4']
                host.ipv6 = addresses['ipv6']
134 135 136 137 138 139 140
                act.result = create_readable(
                    ugettext_noop("Interface successfully created."),
                    ugettext_noop("Interface successfully created. "
                                  "New addresses: ipv4: %(ip4)s, "
                                  "ipv6: %(ip6)s, vlan: %(vlan)s."),
                    ip4=unicode(host.ipv4), ip6=unicode(host.ipv6),
                    vlan=vlan.name)
Őry Máté committed
141
            host.owner = owner
142 143
            if vlan.network_type == 'public':
                host.shared_ip = False
Bach Dániel committed
144
                host.external_ipv4 = None
145 146
            elif vlan.network_type == 'portforward':
                host.shared_ip = True
Bach Dániel committed
147
                host.external_ipv4 = vlan.snat_ip
148
            host.full_clean()
Őry Máté committed
149
            host.save()
150 151 152 153
            host.enable_net()
            from .instance import ACCESS_PROTOCOLS
            port, proto = ACCESS_PROTOCOLS[instance.access_method][1:3]
            host.add_port(proto, private=port)
Őry Máté committed
154 155 156 157 158 159 160
        else:
            host = None

        iface = cls(vlan=vlan, host=host, instance=instance)
        iface.save()
        return iface

161
    def deploy(self):
Guba Sándor committed
162
        queue_name = self.instance.get_remote_queue_name('net', 'fast')
163 164
        return net_tasks.create.apply_async(args=[self.get_vmnetwork_desc()],
                                            queue=queue_name).get()
Dudás Ádám committed
165

166
    def shutdown(self):
Guba Sándor committed
167
        queue_name = self.instance.get_remote_queue_name('net', 'fast')
168 169
        return net_tasks.destroy.apply_async(args=[self.get_vmnetwork_desc()],
                                             queue=queue_name).get()
170

171
    def destroy(self):
172
        if self.host is not None:
Dudás Ádám committed
173 174
            self.host.delete()

Őry Máté committed
175 176 177 178 179 180 181
    def save_as_template(self, instance_template):
        """Create a template based on this interface.
        """
        i = InterfaceTemplate(vlan=self.vlan, managed=self.host is not None,
                              template=instance_template)
        i.save()
        return i