Fix vlan leak
[pharos-tools.git] / dashboard / src / resource_inventory / resource_manager.py
1 ##############################################################################
2 # Copyright (c) 2018 Parker Berberian, Sawyer Bergeron, and others.
3 #
4 # All rights reserved. This program and the accompanying materials
5 # are made available under the terms of the Apache License, Version 2.0
6 # which accompanies this distribution, and is available at
7 # http://www.apache.org/licenses/LICENSE-2.0
8 ##############################################################################
9
10
11 from dashboard.exceptions import (
12     ResourceExistenceException,
13     ResourceAvailabilityException,
14     ResourceProvisioningException,
15     ModelValidationException,
16 )
17 from resource_inventory.models import (
18     Host,
19     HostConfiguration,
20     ResourceBundle,
21     HostProfile,
22     Network,
23     Vlan
24 )
25
26
27 class ResourceManager:
28
29     instance = None
30
31     def __init__(self):
32         pass
33
34     @staticmethod
35     def getInstance():
36         if ResourceManager.instance is None:
37             ResourceManager.instance = ResourceManager()
38         return ResourceManager.instance
39
40     def getAvailableHostTypes(self, lab):
41         hostset = Host.objects.filter(lab=lab).filter(booked=False).filter(working=True)
42         hostprofileset = HostProfile.objects.filter(host__in=hostset, labs=lab)
43         return set(hostprofileset)
44
45     def hostsAvailable(self, grb):
46         """
47         This method will check if the given GenericResourceBundle
48         is available. No changes to the database
49         """
50
51         # count up hosts
52         profile_count = {}
53         for host in grb.getHosts():
54             if host.profile not in profile_count:
55                 profile_count[host.profile] = 0
56             profile_count[host.profile] += 1
57
58         # check that all required hosts are available
59         for profile in profile_count.keys():
60             available = Host.objects.filter(
61                 booked=False,
62                 lab=grb.lab,
63                 profile=profile
64             ).count()
65             needed = profile_count[profile]
66             if available < needed:
67                 return False
68         return True
69
70     # public interface
71     def deleteResourceBundle(self, resourceBundle):
72         for host in Host.objects.filter(bundle=resourceBundle):
73             self.releaseHost(host)
74         resourceBundle.delete()
75
76     def get_vlans(self, genericResourceBundle):
77         networks = {}
78         vlan_manager = genericResourceBundle.lab.vlan_manager
79         for network in genericResourceBundle.networks.all():
80             if network.is_public:
81                 public_net = vlan_manager.get_public_vlan()
82                 vlan_manager.reserve_public_vlan(public_net.vlan)
83                 networks[network.name] = public_net.vlan
84             else:
85                 vlan = vlan_manager.get_vlan()
86                 vlan_manager.reserve_vlans(vlan)
87                 networks[network.name] = vlan
88         return networks
89
90     def convertResourceBundle(self, genericResourceBundle, config=None):
91         """
92         Takes in a GenericResourceBundle and 'converts' it into a ResourceBundle
93         """
94         resource_bundle = ResourceBundle.objects.create(template=genericResourceBundle)
95         generic_hosts = genericResourceBundle.getHosts()
96         physical_hosts = []
97
98         vlan_map = self.get_vlans(genericResourceBundle)
99
100         for generic_host in generic_hosts:
101             host_config = None
102             if config:
103                 host_config = HostConfiguration.objects.get(bundle=config, host=generic_host)
104             try:
105                 physical_host = self.acquireHost(generic_host, genericResourceBundle.lab.name)
106             except ResourceAvailabilityException:
107                 self.fail_acquire(physical_hosts, vlan_map, genericResourceBundle)
108                 raise ResourceAvailabilityException("Could not provision hosts, not enough available")
109             try:
110                 physical_host.bundle = resource_bundle
111                 physical_host.template = generic_host
112                 physical_host.config = host_config
113                 physical_hosts.append(physical_host)
114
115                 self.configureNetworking(physical_host, vlan_map)
116             except Exception:
117                 self.fail_acquire(physical_hosts, vlan_map, genericResourceBundle)
118                 raise ResourceProvisioningException("Network configuration failed.")
119             try:
120                 physical_host.save()
121             except Exception:
122                 self.fail_acquire(physical_hosts, vlan_map, genericResourceBundle)
123                 raise ModelValidationException("Saving hosts failed")
124
125         return resource_bundle
126
127     def configureNetworking(self, host, vlan_map):
128         generic_interfaces = list(host.template.generic_interfaces.all())
129         for int_num, physical_interface in enumerate(host.interfaces.all()):
130             generic_interface = generic_interfaces[int_num]
131             physical_interface.config.clear()
132             for connection in generic_interface.connections.all():
133                 physical_interface.config.add(
134                     Vlan.objects.create(
135                         vlan_id=vlan_map[connection.network.name],
136                         tagged=connection.vlan_is_tagged,
137                         public=connection.network.is_public,
138                         network=connection.network
139                     )
140                 )
141
142     # private interface
143     def acquireHost(self, genericHost, labName):
144         host_full_set = Host.objects.filter(lab__name__exact=labName, profile=genericHost.profile)
145         if not host_full_set.first():
146             raise ResourceExistenceException("No matching servers found")
147         host_set = host_full_set.filter(booked=False)
148         if not host_set.first():
149             raise ResourceAvailabilityException("No unbooked hosts match requested hosts")
150         host = host_set.first()
151         host.booked = True
152         host.template = genericHost
153         host.save()
154         return host
155
156     def releaseHost(self, host):
157         host.template = None
158         host.bundle = None
159         host.booked = False
160         host.save()
161
162     def releaseNetworks(self, grb, vlan_manager, vlans):
163         for net_name, vlan_id in vlans.items():
164             net = Network.objects.get(name=net_name, bundle=grb)
165             if(net.is_public):
166                 vlan_manager.release_public_vlan(vlan_id)
167             else:
168                 vlan_manager.release_vlans(vlan_id)
169
170     def fail_acquire(self, hosts, vlans, grb):
171         vlan_manager = grb.lab.vlan_manager
172         self.releaseNetworks(grb, vlan_manager, vlans)
173         for host in hosts:
174             self.releaseHost(host)