class Subnet(models.Model): SUBNET_NAME_LENGTH = 128 userid = models.CharField('User ID of the owner', max_length=128, null=True, db_index=True) public = models.BooleanField(default=False, db_index=True) network = models.ForeignKey('Network', null=False, db_index=True, related_name="subnets", on_delete=models.PROTECT) name = models.CharField('Subnet Name', max_length=SUBNET_NAME_LENGTH, null=True, default="") ipversion = models.IntegerField('IP Version', default=4, null=False) cidr = models.CharField('Subnet', max_length=64, null=False) gateway = models.CharField('Gateway', max_length=64, null=True) dhcp = models.BooleanField('DHCP', default=True, null=False) deleted = models.BooleanField('Deleted', default=False, db_index=True, null=False) host_routes = fields.SeparatedValuesField('Host Routes', null=True) dns_nameservers = fields.SeparatedValuesField('DNS Nameservers', null=True) created = models.DateTimeField(auto_now_add=True) updated = models.DateTimeField(auto_now=True) def __str__(self): return self.__unicode__() def __unicode__(self): msg = u"<Subnet %s, Network: %s, CIDR: %s>" return msg % (self.id, self.network_id, self.cidr) def get_ip_pools(self, locked=True): ip_pools = self.ip_pools if locked: ip_pools = ip_pools.select_for_update() return map(lambda ip_pool: ip_pool.pool, ip_pools.all())
class Backend(models.Model): clustername = models.CharField('Cluster Name', max_length=128, unique=True) port = models.PositiveIntegerField('Port', default=5080) username = models.CharField('Username', max_length=64, blank=True, null=True) password_hash = models.CharField('Password', max_length=128, blank=True, null=True) # Sha1 is up to 40 characters long hash = models.CharField('Hash', max_length=40, editable=False, null=False) # Unique index of the Backend, used for the mac-prefixes of the # BackendNetworks index = models.PositiveIntegerField('Index', null=False, unique=True, default=0) drained = models.BooleanField('Drained', default=False, null=False) offline = models.BooleanField('Offline', default=False, null=False) # Type of hypervisor hypervisor = models.CharField('Hypervisor', max_length=32, default="kvm", null=False) disk_templates = fields.SeparatedValuesField("Disk Templates", null=True) # Last refresh of backend resources updated = models.DateTimeField(auto_now_add=True) # Backend resources mfree = models.PositiveIntegerField('Free Memory', default=0, null=False) mtotal = models.PositiveIntegerField('Total Memory', default=0, null=False) dfree = models.PositiveIntegerField('Free Disk', default=0, null=False) dtotal = models.PositiveIntegerField('Total Disk', default=0, null=False) pinst_cnt = models.PositiveIntegerField('Primary Instances', default=0, null=False) ctotal = models.PositiveIntegerField('Total number of logical processors', default=0, null=False) HYPERVISORS = ( ("kvm", "Linux KVM hypervisor"), ("xen-pvm", "Xen PVM hypervisor"), ("xen-hvm", "Xen KVM hypervisor"), ) class Meta: verbose_name = u'Backend' ordering = ["clustername"] def __unicode__(self): return self.clustername + "(id=" + str(self.id) + ")" @property def backend_id(self): return self.id def get_client(self): """Get or create a client. """ if self.offline: raise faults.ServiceUnavailable("Backend '%s' is offline" % self) return get_rapi_client(self.id, self.hash, self.clustername, self.port, self.username, self.password) @staticmethod def put_client(client): put_rapi_client(client) def create_hash(self): """Create a hash for this backend. """ sha = sha1('%s%s%s%s' % (self.clustername, self.port, self.username, self.password)) return sha.hexdigest() @property def password(self): return decrypt_db_charfield(self.password_hash) @password.setter def password(self, value): self.password_hash = encrypt_db_charfield(value) def save(self, *args, **kwargs): # Create a new hash each time a Backend is saved old_hash = self.hash self.hash = self.create_hash() super(Backend, self).save(*args, **kwargs) if self.hash != old_hash: # Populate the new hash to the new instances self.virtual_machines.filter(deleted=False)\ .update(backend_hash=self.hash) def __init__(self, *args, **kwargs): super(Backend, self).__init__(*args, **kwargs) if not self.pk: # Generate a unique index for the Backend indexes = Backend.objects.all().values_list('index', flat=True) try: first_free = [x for x in xrange(0, 16) if x not in indexes][0] self.index = first_free except IndexError: raise Exception("Cannot create more than 16 backends") def use_hotplug(self): return self.hypervisor == "kvm" and snf_settings.GANETI_USE_HOTPLUG def get_create_params(self): params = deepcopy(snf_settings.GANETI_CREATEINSTANCE_KWARGS) params["hvparams"] = params.get("hvparams", {})\ .get(self.hypervisor, {}) return params
class Network(models.Model): OPER_STATES = ( ('PENDING', 'Pending'), # Unused because of lazy networks ('ACTIVE', 'Active'), ('DELETED', 'Deleted'), ('ERROR', 'Error') ) ACTIONS = ( ('CREATE', 'Create Network'), ('DESTROY', 'Destroy Network'), ('ADD', 'Add server to Network'), ('REMOVE', 'Remove server from Network'), ) RSAPI_STATE_FROM_OPER_STATE = { 'PENDING': 'PENDING', 'ACTIVE': 'ACTIVE', 'DELETED': 'DELETED', 'ERROR': 'ERROR' } FLAVORS = { 'CUSTOM': { 'mode': 'bridged', 'link': settings.DEFAULT_BRIDGE, 'mac_prefix': settings.DEFAULT_MAC_PREFIX, 'tags': None, 'desc': "Basic flavor used for a bridged network", }, 'IP_LESS_ROUTED': { 'mode': 'routed', 'link': None, 'mac_prefix': settings.DEFAULT_MAC_PREFIX, 'tags': 'ip-less-routed', 'desc': "Flavor used for an IP-less routed network using" " Proxy ARP", }, 'MAC_FILTERED': { 'mode': 'bridged', 'link': settings.DEFAULT_MAC_FILTERED_BRIDGE, 'mac_prefix': 'pool', 'tags': 'private-filtered', 'desc': "Flavor used for bridged networks that offer isolation" " via filtering packets based on their src " " MAC (ebtables)", }, 'PHYSICAL_VLAN': { 'mode': 'bridged', 'link': 'pool', 'mac_prefix': settings.DEFAULT_MAC_PREFIX, 'tags': 'physical-vlan', 'desc': "Flavor used for bridged network that offer isolation" " via dedicated physical vlan", }, } NETWORK_NAME_LENGTH = 128 objects = NetworkManager() name = models.CharField('Network Name', max_length=NETWORK_NAME_LENGTH) userid = models.CharField('User ID of the owner', max_length=128, null=True, db_index=True) project = models.CharField(max_length=255, null=True, db_index=True) shared_to_project = models.BooleanField('Shared to project', default=False) flavor = models.CharField('Flavor', max_length=32, null=False) mode = models.CharField('Network Mode', max_length=16, null=True) link = models.CharField('Network Link', max_length=32, null=True) mac_prefix = models.CharField('MAC Prefix', max_length=32, null=False) tags = models.CharField('Network Tags', max_length=128, null=True) public = models.BooleanField(default=False, db_index=True) created = models.DateTimeField(auto_now_add=True) updated = models.DateTimeField(auto_now=True) deleted = models.BooleanField('Deleted', default=False, db_index=True) state = models.CharField(choices=OPER_STATES, max_length=32, default='PENDING') machines = models.ManyToManyField(VirtualMachine, through='NetworkInterface') action = models.CharField(choices=ACTIONS, max_length=32, null=True, default=None) drained = models.BooleanField("Drained", default=False, null=False) floating_ip_pool = models.BooleanField('Floating IP Pool', null=False, default=False) external_router = models.BooleanField(default=False) serial = models.ForeignKey(QuotaHolderSerial, related_name='network', null=True, on_delete=models.SET_NULL) subnet_ids = fields.SeparatedValuesField("Subnet IDs", null=True) def __str__(self): return self.__unicode__() def __unicode__(self): return u"<Network: %s>" % self.id @property def backend_id(self): """Return the backend id by prepending backend-prefix.""" if not self.id: raise Network.InvalidBackendIdError("self.id is None") return "%snet-%s" % (settings.BACKEND_PREFIX_ID, str(self.id)) @property def backend_tag(self): """Return the network tag to be used in backend """ if self.tags: return self.tags.split(',') else: return [] def create_backend_network(self, backend=None): """Create corresponding BackendNetwork entries.""" backends = [backend] if backend else\ Backend.objects.filter(offline=False) for backend in backends: backend_exists =\ BackendNetwork.objects.filter(backend=backend, network=self)\ .exists() if not backend_exists: BackendNetwork.objects.create(backend=backend, network=self) def get_ip_pools(self, locked=True): subnets = self.subnets.filter(ipversion=4, deleted=False)\ .prefetch_related("ip_pools") return [ip_pool for subnet in subnets for ip_pool in subnet.get_ip_pools(locked=locked)] def reserve_address(self, address, external=False): for ip_pool in self.get_ip_pools(): if ip_pool.contains(address): ip_pool.reserve(address, external=external) ip_pool.save() return raise pools.InvalidValue("Network %s does not have an IP pool that" " contains address %s" % (self, address)) def release_address(self, address, external=False): for ip_pool in self.get_ip_pools(): if ip_pool.contains(address): ip_pool.put(address, external=external) ip_pool.save() return raise pools.InvalidValue("Network %s does not have an IP pool that" " contains address %s" % (self, address)) @property def subnet4(self): return self.get_subnet(version=4) @property def subnet6(self): return self.get_subnet(version=6) def get_subnet(self, version=4): for subnet in self.subnets.all(): if subnet.ipversion == version: return subnet return None def ip_count(self): """Return the total and free IPv4 addresses of the network.""" total, free = 0, 0 ip_pools = self.get_ip_pools(locked=False) for ip_pool in ip_pools: total += ip_pool.pool_size free += ip_pool.count_available() return total, free class InvalidBackendIdError(ValueError): def __init__(self, value): self.value = value def __str__(self): return repr(self.value) class InvalidBackendMsgError(Exception): def __init__(self, opcode, status): self.opcode = opcode self.status = status def __str__(self): return repr('<opcode: %s, status: %s>' % (self.opcode, self.status)) class InvalidActionError(Exception): def __init__(self, action): self._action = action def __str__(self): return repr(str(self._action))