network.py 6.99 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

from logging import getLogger
from netaddr import EUI, mac_unix

23
from django.db.models import Model, ForeignKey, BooleanField, CharField
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

class Interface(Model):

    """Network interface for an instance.
    """
64 65 66
    MODEL_TYPES = (('virtio', 'virtio'), ('ne2k_pci', 'ne2k_pci'),
                   ('pcnet', 'pcnet'), ('rtl8139', 'rtl8139'),
                   ('e1000', 'e1000'))
Őry Máté committed
67 68 69 70 71
    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')
72
    model = CharField(max_length=10, choices=MODEL_TYPES, default='virtio')
Őry Máté committed
73 74 75 76

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

    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
96 97
        class mac_custom(mac_unix):
            word_fmt = '%.2X'
Őry Máté committed
98 99 100
        i = instance.id & 0xfffffff
        v = vlan.vid & 0xfff
        m = (0x02 << 40) | (i << 12) | v
101
        return EUI(m, dialect=mac_custom)
Őry Máté committed
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,
111
            'model': self.model,
Őry Máté committed
112 113 114 115
            'managed': self.host is not None
        }

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

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

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

171
    def shutdown(self):
Guba Sándor committed
172
        queue_name = self.instance.get_remote_queue_name('net', 'fast')
173 174
        return net_tasks.destroy.apply_async(args=[self.get_vmnetwork_desc()],
                                             queue=queue_name).get()
175

176
    def destroy(self):
177
        if self.host is not None:
Dudás Ádám committed
178 179
            self.host.delete()

Őry Máté committed
180 181 182 183 184 185 186
    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