network.py 6.77 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
from firewall.models import Vlan, Host
from ..tasks import net_tasks
29
from .activity import instance_activity
Őry Máté committed
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 56

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')

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

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

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'
74
        ordering = ("-vlan__managed", )
Őry Máté committed
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92

    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
93 94
        class mac_custom(mac_unix):
            word_fmt = '%.2X'
Őry Máté committed
95 96 97
        i = instance.id & 0xfffffff
        v = vlan.vid & 0xfff
        m = (0x02 << 40) | (i << 12) | v
98
        return EUI(m, dialect=mac_custom)
Őry Máté committed
99 100 101 102 103 104 105 106 107 108 109 110 111

    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
112
    def create(cls, instance, vlan, managed, owner=None, base_activity=None):
Őry Máté committed
113 114 115 116 117 118 119 120
        """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
121
            # Get addresses from firewall
122
            if base_activity is None:
123 124 125 126
                act_ctx = instance_activity(
                    code_suffix='allocating_ip',
                    readable_name=ugettext_noop("allocate IP address"),
                    instance=instance, user=owner)
127
            else:
128 129 130
                act_ctx = base_activity.sub_activity(
                    'allocating_ip',
                    readable_name=ugettext_noop("allocate IP address"))
131
            with act_ctx as act:
132 133 134
                addresses = vlan.get_new_address()
                host.ipv4 = addresses['ipv4']
                host.ipv6 = addresses['ipv6']
135 136 137 138 139 140 141
                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
142
            host.owner = owner
143 144
            if vlan.network_type == 'public':
                host.shared_ip = False
Bach Dániel committed
145
                host.external_ipv4 = None
146 147
            elif vlan.network_type == 'portforward':
                host.shared_ip = True
Bach Dániel committed
148
                host.external_ipv4 = vlan.snat_ip
149
            host.full_clean()
Őry Máté committed
150
            host.save()
151 152 153 154
            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
155 156 157 158 159 160 161
        else:
            host = None

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

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

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

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

Őry Máté committed
176 177 178 179 180 181 182
    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