class DiskDevice(models.Model): class Meta(object): db_table = 'devops_diskdevice' app_label = 'devops' node = models.ForeignKey('Node', null=False) volume = models.ForeignKey('Volume', null=True) device = choices('disk', 'cdrom') type = choices('file') bus = choices('virtio') target_dev = models.CharField(max_length=255, null=False) @classmethod def node_attach_volume(cls, node, volume, device='disk', type='file', bus='virtio', target_dev=None): """Attach volume to node :rtype : DiskDevice """ return cls.objects.create(device=device, type=type, bus=bus, target_dev=target_dev or node.next_disk_name(), volume=volume, node=node)
class Interface(models.Model): class Meta(object): db_table = 'devops_interface' app_label = 'devops' node = models.ForeignKey('Node') l2_network_device = models.ForeignKey('L2NetworkDevice', null=True) label = models.CharField(max_length=255, null=True) mac_address = models.CharField(max_length=255, unique=True, null=False) type = models.CharField(max_length=255, null=False) model = choices('virtio', 'e1000', 'pcnet', 'rtl8139', 'ne2k_pci') # LEGACY, for fuel-qa compatibility if MULTIPLE_NETWORKS enabled @property def network(self): return self.l2_network_device @property def target_dev(self): return self.label @property def addresses(self): return self.address_set.all() def add_address(self): ip = self.l2_network_device.address_pool.next_ip() Address.objects.create( ip_address=str(ip), interface=self, ) @staticmethod def interface_create(l2_network_device, node, label, type='network', mac_address=None, model='virtio'): """Create interface :rtype : Interface """ interface = Interface.objects.create( l2_network_device=l2_network_device, node=node, label=label, type=type, mac_address=mac_address or generate_mac(), model=model) if (interface.l2_network_device and interface.l2_network_device.address_pool is not None): interface.add_address() return interface
class Interface(models.Model): class Meta(object): db_table = 'devops_interface' network = models.ForeignKey('Network') node = models.ForeignKey('Node') mac_address = models.CharField(max_length=255, unique=True, null=False) type = models.CharField(max_length=255, null=False) model = choices('virtio', 'e1000', 'pcnet', 'rtl8139', 'ne2k_pci') @property def target_dev(self): return self.node.driver.node_get_interface_target_dev( self.node, self.mac_address) @property def addresses(self): return self.address_set.all() @staticmethod def interface_create(network, node, type='network', mac_address=None, model='virtio', interface_map=None): """Create interface :rtype : Interface """ if interface_map is None: interface_map = {} interfaces = [] def _create(mac_addr=None): interface = Interface.objects.create(network=network, node=node, type=type, mac_address=mac_addr or generate_mac(), model=model) Address.objects.create(ip_address=str(network.next_ip()), interface=interface) return interface if interface_map: if len(interface_map[network.name]) > 0: for _ in interface_map[network.name]: interfaces.append(_create()) return interfaces else: return _create(mac_address)
class Interface(ParamedModel): class Meta(object): db_table = 'devops_interface' app_label = 'devops' node = models.ForeignKey('Node') l2_network_device = models.ForeignKey('L2NetworkDevice', null=True) label = models.CharField(max_length=255, null=True) mac_address = models.CharField(max_length=255, unique=True, null=False) type = models.CharField(max_length=255, null=False) model = choices('virtio', 'e1000', 'pcnet', 'rtl8139', 'ne2k_pci') @property def driver(self): return self.node.driver # LEGACY, for fuel-qa compatibility if MULTIPLE_NETWORKS enabled @property def network(self): return self.l2_network_device @property def target_dev(self): return self.label @property def addresses(self): return self.address_set.all() @property def network_config(self): return self.node.networkconfig_set.get(label=self.label) def define(self): self.save() def remove(self): self.delete() def add_address(self): ip = self.l2_network_device.address_pool.next_ip() Address.objects.create( ip_address=str(ip), interface=self, ) @property def is_blocked(self): """Show state of interface""" return False def block(self): """Block traffic on interface""" pass def unblock(self): """Unblock traffic on interface""" pass @classmethod def interface_create(cls, l2_network_device, node, label, if_type='network', mac_address=None, model='virtio'): """Create interface :rtype : Interface """ interface = cls.objects.create(l2_network_device=l2_network_device, node=node, label=label, type=if_type, mac_address=mac_address or generate_mac(), model=model) if (interface.l2_network_device and interface.l2_network_device.address_pool is not None): interface.add_address() return interface
class Network(DriverModel): class Meta: unique_together = ('name', 'environment') db_table = 'devops_network' environment = models.ForeignKey('Environment', null=True) name = models.CharField(max_length=255, unique=False, null=False) uuid = models.CharField(max_length=255) has_dhcp_server = models.BooleanField() has_pxe_server = models.BooleanField() has_reserved_ips = models.BooleanField(default=True) tftp_root_dir = models.CharField(max_length=255) forward = choices('nat', 'route', 'bridge', 'private', 'vepa', 'passthrough', 'hostdev', null=True) # 'ip_network' should be renamed to 'cidr' ip_network = models.CharField(max_length=255, unique=True) _iterhosts = None # Dirty trick. It should be placed on instance level of Environment class. default_pool = None @property def ip(self): """Return IPNetwork representation of self.ip_network field. :return: IPNetwork() """ return IPNetwork(self.ip_network) @property def interfaces(self): return self.interface_set.all() @property def ip_pool_start(self): return IPNetwork(self.ip_network)[2] @property def ip_pool_end(self): return IPNetwork(self.ip_network)[-2] @property def netmask(self): return IPNetwork(self.ip_network).netmask @property def default_gw(self): return IPNetwork(self.ip_network)[1] def next_ip(self): while True: self._iterhosts = self._iterhosts or IPNetwork( self.ip_network).iterhosts() ip = self._iterhosts.next() if ip < self.ip_pool_start or ip > self.ip_pool_end: continue if not Address.objects.filter(interface__network=self, ip_address=str(ip)).exists(): return ip def bridge_name(self): return self.driver.network_bridge_name(self) def define(self): self.driver.network_define(self) self.save() def start(self): self.create(verbose=False) def create(self, verbose=False): if verbose or not self.driver.network_active(self): self.driver.network_create(self) def destroy(self): self.driver.network_destroy(self) def erase(self): self.remove(verbose=False) def remove(self, verbose=False): if verbose or self.uuid: if verbose or self.driver.network_exists(self): if self.driver.network_active(self): self.driver.network_destroy(self) self.driver.network_undefine(self) self.delete() @classmethod def create_network_pool(cls, networks, prefix): """Create network pool :rtype : IpNetworksPool """ pool = IpNetworksPool(networks=networks, prefix=prefix) pool.set_allocated_networks(cls.get_driver().get_allocated_networks()) return pool @classmethod def _get_default_pool(cls): """Get default pool. If it does not exists, create 10.0.0.0/16 pool. :rtype : IpNetworksPool """ cls.default_pool = cls.default_pool or Network.create_network_pool( networks=[IPNetwork('10.0.0.0/16')], prefix=24) return cls.default_pool @classmethod @transaction.commit_on_success def _safe_create_network(cls, name, environment=None, pool=None, has_dhcp_server=True, has_pxe_server=False, forward='nat'): allocated_pool = pool or cls._get_default_pool() while True: try: ip_network = allocated_pool.next() if not cls.objects.filter(ip_network=str(ip_network)).exists(): return cls.objects.create(environment=environment, name=name, ip_network=ip_network, has_pxe_server=has_pxe_server, has_dhcp_server=has_dhcp_server, forward=forward) except IntegrityError: transaction.rollback() @classmethod def network_create(cls, name, environment=None, ip_network=None, pool=None, has_dhcp_server=True, has_pxe_server=False, forward='nat'): """Create network :rtype : Network """ if ip_network: return cls.objects.create(environment=environment, name=name, ip_network=ip_network, has_pxe_server=has_pxe_server, has_dhcp_server=has_dhcp_server, forward=forward) return cls._safe_create_network(environment=environment, forward=forward, has_dhcp_server=has_dhcp_server, has_pxe_server=has_pxe_server, name=name, pool=pool) @classmethod def create_networks(cls, environment, network_names=None, has_dhcp=False, has_pxe=False, forward='nat', pool=None): """Create several networks :param environment: Environment :param network_names: List :param has_dhcp: Bool :param has_pxe: Bool :param forward: String :param pool: IpNetworksPool :rtype : List """ if network_names is None: network_names = settings.DEFAULT_INTERFACE_ORDER.split(',') networks = [] for name in network_names: net = cls.network_create(name=name, environment=environment, has_dhcp_server=has_dhcp, has_pxe_server=has_pxe, forward=forward, pool=pool) networks.append(net) return networks
class Interface(base.ParamedModel): """Describes a network interface configuration Specify the abstract label of the interface (you can use labels that match the real interface names or use any suitable names). 'l2_network_device' describes the switch name (virtual or hardware) to which the interface is connected. 'features' is a json list with any strings you want use to mark interfaces with specific features and use these marks in 3rd-party libraries to perform highlevel configuration of the right interfaces for your product. Template example (interfaces): --------------------------------- - name: some_node_name params: ... interfaces: - label: iface0 l2_network_device: admin - label: iface1 l2_network_device: data mac_address: !os_env IFACE_MAC_ADDRESS, 00:11:22:33:44:55 features: ['dpdk', 'dpdk_pci: 0000:05:00.1'] """ class Meta(object): db_table = 'devops_interface' app_label = 'devops' node = models.ForeignKey('Node') l2_network_device = models.ForeignKey('L2NetworkDevice', null=True) label = models.CharField(max_length=255, null=True) mac_address = models.CharField(max_length=255, unique=True, null=False) type = models.CharField(max_length=255, null=False) model = base.choices('virtio', 'e1000', 'pcnet', 'rtl8139', 'ne2k_pci') features = base.ParamField(default=[]) @property def driver(self): return self.node.driver # LEGACY, for fuel-qa compatibility if MULTIPLE_NETWORKS enabled @property def network(self): return self.l2_network_device @property def target_dev(self): return self.label @property def addresses(self): return self.address_set.all() @property def network_config(self): return self.node.networkconfig_set.get(label=self.label) def define(self): self.save() def remove(self): self.delete() def add_address(self): """Assign an IP address to the interface Try to get an IP from reserved IP with name '<group>_<node>' , or generate next IP if reserved IP wasn't found. Next IP is generated from the DHCP ip_range, or from the network range [+2:-2] of all available addresses in the address pool. """ reserved_ip_name = helpers.underscored(self.node.group.name, self.node.name) reserved_ip = self.l2_network_device.address_pool.get_ip( reserved_ip_name) ip = reserved_ip or self.l2_network_device.address_pool.next_ip() Address.objects.create( ip_address=str(ip), interface=self, ) @property def is_blocked(self): """Show state of interface""" return False def block(self): """Block traffic on interface""" pass def unblock(self): """Unblock traffic on interface""" pass @classmethod def interface_create(cls, l2_network_device, node, label, if_type='network', mac_address=None, model='virtio', features=None): """Create interface :rtype : Interface """ interface = cls.objects.create( l2_network_device=l2_network_device, node=node, label=label, type=if_type, mac_address=mac_address or helpers.generate_mac(), model=model, features=features or []) if (interface.l2_network_device and interface.l2_network_device.address_pool is not None): interface.add_address() return interface
class Node(DriverModel): class Meta: unique_together = ('name', 'environment') db_table = 'devops_node' environment = models.ForeignKey('Environment', null=True) name = models.CharField(max_length=255, unique=False, null=False) uuid = models.CharField(max_length=255) hypervisor = choices('kvm') os_type = choices('hvm') architecture = choices('x86_64', 'i686') boot = models.CharField(max_length=255, null=False, default=json.dumps([])) metadata = models.CharField(max_length=255, null=True) role = models.CharField(max_length=255, null=True) vcpu = models.PositiveSmallIntegerField(null=False, default=1) memory = models.IntegerField(null=False, default=1024) has_vnc = models.BooleanField(null=False, default=True) def next_disk_name(self): disk_names = ('sd' + c for c in list('abcdefghijklmnopqrstuvwxyz')) while True: disk_name = disk_names.next() if not self.disk_devices.filter(target_dev=disk_name).exists(): return disk_name def get_vnc_port(self): return self.driver.node_get_vnc_port(node=self) @property def disk_devices(self): return self.diskdevice_set.all() @property def interfaces(self): return self.interface_set.order_by('id') @property def vnc_password(self): return settings.VNC_PASSWORD def interface_by_name(self, name): self.interfaces.filter(name=name) def get_ip_address_by_network_name(self, name, interface=None): interface = interface or self.interface_set.filter( network__name=name).order_by('id')[0] return interface.address_set.get(interface=interface).ip_address def remote(self, network_name, login, password=None, private_keys=None): """Create SSH-connection to the network :rtype : SSHClient """ return SSHClient(self.get_ip_address_by_network_name(network_name), username=login, password=password, private_keys=private_keys) def send_keys(self, keys): self.driver.node_send_keys(self, keys) def await (self, network_name, timeout=120, by_port=22): _wait(lambda: _tcp_ping( self.get_ip_address_by_network_name(network_name), by_port), timeout=timeout) def define(self): self.driver.node_define(self) self.save() def start(self): self.create(verbose=False) def create(self, verbose=False): if verbose or not self.driver.node_active(self): self.driver.node_create(self) def destroy(self, verbose=False): if verbose or self.driver.node_active(self): self.driver.node_destroy(self) def erase(self): self.remove(verbose=False) def remove(self, verbose=False): if verbose or self.uuid: if verbose or self.driver.node_exists(self): self.destroy(verbose=False) self.driver.node_undefine(self, undefine_snapshots=True) self.delete() def suspend(self, verbose=False): if verbose or self.driver.node_active(self): self.driver.node_suspend(self) def resume(self, verbose=False): if verbose or self.driver.node_active(self): self.driver.node_resume(self) def has_snapshot(self, name): return self.driver.node_snapshot_exists(node=self, name=name) def snapshot(self, name=None, force=False, description=None): if force and self.has_snapshot(name): self.driver.node_delete_snapshot(node=self, name=name) self.driver.node_create_snapshot(node=self, name=name, description=description) def revert(self, name=None, destroy=True): if destroy: self.destroy(verbose=False) if self.has_snapshot(name): self.driver.node_revert_snapshot(node=self, name=name) else: print('Domain snapshot for {0} node not found: no domain ' 'snapshot with matching' ' name {1}'.format(self.name, name)) def get_snapshots(self): return self.driver.node_get_snapshots(node=self) def erase_snapshot(self, name): self.driver.node_delete_snapshot(node=self, name=name) def set_vcpu(self, vcpu): """Set vcpu count on node param: vcpu: Integer :rtype : None """ if vcpu != self.vcpu: self.vcpu = vcpu self.driver.node_set_vcpu(node=self, vcpu=vcpu) self.save() def set_memory(self, memory): """Set memory size on node param: memory: Integer :rtype : None """ if memory != self.memory: self.memory = memory self.driver.node_set_memory(node=self, memory=memory * 1024) self.save() def attach_to_networks(self, network_names=None): """Attache node to several networks param: network_names: List :rtype : None """ if network_names is None: network_names = settings.DEFAULT_INTERFACE_ORDER.split(',') networks = self.environment.get_networks(name=network_names) self.environment.create_interfaces(networks=networks, node=self) def attach_disks(self, disknames_capacity=None, format='qcow2', device='disk', bus='virtio', force_define=False): """Attach several disks to node param: disknames_capacity: Dict param: format: String param: device: String param: bus: String param: force_define: Bool :rtype : None """ if disknames_capacity is None: disknames_capacity = { 'system': 50 * 1024**3, 'swift': 50 * 1024**3, 'cinder': 50 * 1024**3, } for diskname, capacity in disknames_capacity.iteritems(): self.attach_disk(name=diskname, capacity=capacity, force_define=force_define) def attach_disk(self, name, capacity, format='qcow2', device='disk', bus='virtio', force_define=False): """Attach disk to node param: disknames_capacity: Dict param: format: String param: device: String param: bus: String param: force_define: Bool :rtype : DiskDevice """ vol_name = "%s-%s" % (self.name, name) disk = self.environment.add_empty_volume(node=self, name=vol_name, capacity=capacity, device=device, bus=bus) if force_define: disk.volume.define() return disk @classmethod def node_create(cls, name, environment=None, role=None, vcpu=1, memory=1024, has_vnc=True, metadata=None, hypervisor='kvm', os_type='hvm', architecture='x86_64', boot=None): """Create node :rtype : Node """ if not boot: boot = ['network', 'cdrom', 'hd'] node = cls.objects.create(name=name, environment=environment, role=role, vcpu=vcpu, memory=memory, has_vnc=has_vnc, metadata=metadata, hypervisor=hypervisor, os_type=os_type, architecture=architecture, boot=json.dumps(boot)) return node
class Interface(base.ParamedModel): class Meta(object): db_table = 'devops_interface' app_label = 'devops' node = models.ForeignKey('Node') l2_network_device = models.ForeignKey('L2NetworkDevice', null=True) label = models.CharField(max_length=255, null=True) mac_address = models.CharField(max_length=255, unique=True, null=False) type = models.CharField(max_length=255, null=False) model = base.choices('virtio', 'e1000', 'pcnet', 'rtl8139', 'ne2k_pci') features = base.ParamField(default=[]) @property def driver(self): return self.node.driver # LEGACY, for fuel-qa compatibility if MULTIPLE_NETWORKS enabled @property def network(self): return self.l2_network_device @property def target_dev(self): return self.label @property def addresses(self): return self.address_set.all() @property def network_config(self): return self.node.networkconfig_set.get(label=self.label) def define(self): self.save() def remove(self): self.delete() def add_address(self): """Assign an IP address to the interface Try to get an IP from reserved IP with name '<group>_<node>' , or generate next IP if reserved IP wasn't found. Next IP is generated from the DHCP ip_range, or from the network range [+2:-2] of all available addresses in the address pool. """ reserved_ip_name = helpers.underscored(self.node.group.name, self.node.name) reserved_ip = self.l2_network_device.address_pool.get_ip( reserved_ip_name) ip = reserved_ip or self.l2_network_device.address_pool.next_ip() Address.objects.create( ip_address=str(ip), interface=self, ) @property def is_blocked(self): """Show state of interface""" return False def block(self): """Block traffic on interface""" pass def unblock(self): """Unblock traffic on interface""" pass @classmethod def interface_create(cls, l2_network_device, node, label, if_type='network', mac_address=None, model='virtio', features=None): """Create interface :rtype : Interface """ interface = cls.objects.create(l2_network_device=l2_network_device, node=node, label=label, type=if_type, mac_address=mac_address or helpers.generate_mac(), model=model, features=features or []) if (interface.l2_network_device and interface.l2_network_device.address_pool is not None): interface.add_address() return interface