network.py 6.4 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 24 25 26 27

from logging import getLogger
from netaddr import EUI, mac_unix

from django.db.models import Model, ForeignKey, BooleanField
from django.utils.translation import ugettext_lazy as _

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

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 123
                act_ctx = instance_activity(code_suffix='allocating_ip',
                                            instance=instance, user=owner)
124
            else:
125 126
                act_ctx = base_activity.sub_activity('allocating_ip')
            with act_ctx as act:
127 128 129 130 131 132 133
                addresses = vlan.get_new_address()
                host.ipv4 = addresses['ipv4']
                host.ipv6 = addresses['ipv6']
                act.result = ('new addresses: ipv4: %(ip4)s, ipv6: %(ip6)s, '
                              'vlan: %(vlan)s' % {'ip4': host.ipv4,
                                                  'ip6': host.ipv6,
                                                  'vlan': vlan.name})
Őry Máté committed
134
            host.owner = owner
135 136
            if vlan.network_type == 'public':
                host.shared_ip = False
Bach Dániel committed
137
                host.external_ipv4 = None
138 139
            elif vlan.network_type == 'portforward':
                host.shared_ip = True
Bach Dániel committed
140
                host.external_ipv4 = vlan.snat_ip
141
            host.full_clean()
Őry Máté committed
142
            host.save()
143 144 145 146
            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
147 148 149 150 151 152 153
        else:
            host = None

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

154
    def deploy(self):
155 156 157
        queue_name = self.instance.get_remote_queue_name('net')
        return net_tasks.create.apply_async(args=[self.get_vmnetwork_desc()],
                                            queue=queue_name).get()
Dudás Ádám committed
158

159
    def shutdown(self):
160
        queue_name = self.instance.get_remote_queue_name('net')
161 162
        return net_tasks.destroy.apply_async(args=[self.get_vmnetwork_desc()],
                                             queue=queue_name).get()
163

164
    def destroy(self):
165
        if self.host is not None:
Dudás Ádám committed
166 167
            self.host.delete()

Őry Máté committed
168 169 170 171 172 173 174
    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