def creating(self, model: Zone): defer(model.save) with self.vmware.client_session() as vmware_client: datacenter = self.vmware.get_datacenter(vmware_client, model.region.datacenter) datastore = self.vmware.get_datastore(vmware_client, model.vm_datastore, datacenter) if datastore is None: model.error_message = "Could not find VMWare datastore." return cluster = self.vmware.get_cluster(vmware_client, model.vm_cluster, datacenter) if cluster is None: model.error_message = "Could not find VMWare cluster." return if model.vm_folder is not None: folder = self.vmware.get_folder(vmware_client, model.vm_folder, datacenter) if folder is None: model.error_message = "Could not find VMWare VM & Templates folder." return model.state = ResourceState.Created
def find_best_zone(self, vmware_client, datacenter, region, model): """ Currently this just finds a zone that can host the instances and returns that. We may want a better way to balance instances across zones in the future """ zones = Zone.list(label_selector=REGION_LABEL + '=' + str(region.name)) for zone in zones: if zone.schedulable is False: continue if self.can_zone_host(vmware_client, datacenter, zone, model): return zone return None
def create(self): """Create a volume --- post: description: Create a volume tags: - compute - volume requestBody: description: Volume to create responses: 200: description: The created volume """ request: RequestCreateVolume = cherrypy.request.model project: Project = cherrypy.request.project volume = Volume.get(project, request.name) if volume is not None: raise cherrypy.HTTPError( 409, 'A volume with the requested name already exists.') zone = Zone.get(request.zone_name) if zone is None: raise cherrypy.HTTPError( 404, 'A zone with the requested name does not exist.') if zone.state != ResourceState.Created: raise cherrypy.HTTPError( 400, 'Can only create a volume with a zone in the following state: {0}' .format(ResourceState.Created.value)) quota: ProjectQuota = ProjectQuota.get(project.name) used_disk = quota.used_disk + request.size if quota.disk != -1: if used_disk > quota.disk: raise QuotaError("Disk", request.size, quota.used_disk, quota.disk) quota.used_disk = used_disk quota.save() volume = Volume() volume.project = project volume.name = request.name volume.zone = zone volume.size = request.size volume.create() return ResponseVolume.from_database(volume)
def can_delete(self, model): # These resources need the region to exist to successfully delete zones = Zone.list(label_selector=REGION_LABEL + "=" + model.name) if len(zones) > 0: return False images = Image.list_all(label_selector=REGION_LABEL + "=" + model.name) if len(images) > 0: return False instances = Instance.list_all(label_selector=REGION_LABEL + "=" + model.name) if len(instances) > 0: return False return True
def list(self, image_name, region_name, zone_name, limit: int, marker: uuid.UUID): """List instances --- get: description: List instances tags: - compute - instance responses: 200: description: List of instances """ kwargs = { 'project': cherrypy.request.project, 'label_selector': [], } if image_name is not None: image: Image = Image.get(cherrypy.request.project, image_name) if image is None: raise cherrypy.HTTPError(404, "An image with the requested name does not exist.") kwargs['label_selector'].append(IMAGE_LABEL + '=' + image.name) if region_name is not None: region: Region = Region.get(region_name) if region is None: raise cherrypy.HTTPError(404, "A region with the requested name does not exist.") kwargs['label_selector'].append(REGION_LABEL + '=' + region.name) if zone_name is not None: zone: Zone = Zone.get(zone_name) if zone is None: raise cherrypy.HTTPError(404, 'A zone with the requested name does not exist.') kwargs['label_selector'].append(ZONE_LABEL + '=' + zone.name) if len(kwargs['label_selector']) > 0: kwargs['label_selector'] = ",".join(kwargs['label_selector']) else: del kwargs['label_selector'] return self.paginate(Instance, ResponseInstance, limit, marker, **kwargs)
def zone(self): zone_name = self.zone_name if zone_name is None: return None return Zone.get(zone_name)
def run(self, args) -> int: cache_client.connect(url=args.redis_url) if args.kube_config != "" or args.kube_master != "": self.logger.info("Using kube-config configuration") Configuration.set_default(Configuration()) if args.kube_config != "": config.load_kube_config(config_file=args.kube_config) if args.kube_master != "": Configuration._default.host = args.kube_master else: self.logger.info("Using in-cluster configuration") config.load_incluster_config() while True: try: client.CoreV1Api().list_namespace() break except urllib3.exceptions.HTTPError as e: self.logger.error("Error connecting to the Kubernetes API. Trying again in 5 seconds. Error: " + str(e)) time.sleep(5) old_json_encoder = json.JSONEncoder.default def json_encoder(self, o): # pragma: no cover if isinstance(o, uuid.UUID): return str(o) if isinstance(o, arrow.Arrow): return o.isoformat() if isinstance(o, ipaddress.IPv4Network): return str(o) if isinstance(o, ipaddress.IPv4Address): return str(o) if isinstance(o, enum.Enum): return o.value if isinstance(o, datetime.datetime): return str(o.isoformat()) return old_json_encoder(self, o) json.JSONEncoder.default = json_encoder self.logger.info("Creating CRDs") IAMSystemRole.create_crd() IAMSystemRole.wait_for_crd() IAMProjectRole.create_crd() IAMProjectRole.wait_for_crd() IAMPolicy.create_crd() IAMPolicy.wait_for_crd() IAMPolicy.create_system_policy() SystemServiceAccount.create_crd() SystemServiceAccount.wait_for_crd() ProjectServiceAccount.create_crd() ProjectServiceAccount.wait_for_crd() IAMSystemRole.create_default_roles() SystemServiceAccount.create_admin_sa() ProjectQuota.create_crd() ProjectQuota.wait_for_crd() Region.create_crd() Region.wait_for_crd() Zone.create_crd() Zone.wait_for_crd() Network.create_crd() Network.wait_for_crd() NetworkPort.create_crd() NetworkPort.wait_for_crd() Image.create_crd() Image.wait_for_crd() Flavor.create_crd() Flavor.wait_for_crd() Volume.create_crd() Volume.wait_for_crd() Instance.create_crd() Instance.wait_for_crd() Keypair.create_crd() Keypair.wait_for_crd() self.logger.info("CRDs have been created") self.menu_url = args.menu_url self.vmware = VMWare(args.vcenter_host, args.vcenter_port, args.vcenter_username, args.vcenter_password) self.leader_elector = LeaderElector("sandwich-controller", "kube-system", self.on_started_leading, self.on_stopped_leading) self.leader_elector.start() return 0
def create(self): """Create an instance --- post: description: Create an instance tags: - compute - instance requestBody: description: Instance to create responses: 200: description: The created instance """ request: RequestCreateInstance = cherrypy.request.model project: Project = cherrypy.request.project instance = Instance.get(project, request.name) if instance is not None: raise cherrypy.HTTPError(409, 'An instance with the requested name already exists.') region = Region.get(request.region_name) if region is None: raise cherrypy.HTTPError(404, 'A region with the requested name does not exist.') if region.state != ResourceState.Created: raise cherrypy.HTTPError(400, 'Can only create a instance with a region in the following state: {0}'.format( ResourceState.Created.value)) zone = None if request.zone_name is not None: zone = Zone.get(request.zone_name) if zone is None: raise cherrypy.HTTPError(404, 'A zone with the requested name does not exist.') if zone.region.name != region.name: raise cherrypy.HTTPError(409, 'The requested zone is not within the requested region') if zone.state != ResourceState.Created: raise cherrypy.HTTPError(400, 'Can only create a instance with a zone in the following state: {0}'.format( ResourceState.Created.value)) network = Network.get(request.network_name) if network is None: raise cherrypy.HTTPError(404, 'A network with the requested name does not exist.') if network.region.name != region.name: raise cherrypy.HTTPError(409, 'The requested network is not within the requested region') if network.state != ResourceState.Created: raise cherrypy.HTTPError(400, 'Can only create a instance with a network in the following state: {0}'.format( ResourceState.Created.value)) # TODO: User inputs image url instead of id # projectId/imageId image: Image = Image.get(project, request.image_name) if image is None: raise cherrypy.HTTPError(404, 'An image with the requested name does not exist.') if image.region.name != region.name: raise cherrypy.HTTPError(409, 'The requested image is not within the requested region') if image.state != ResourceState.Created: raise cherrypy.HTTPError(400, 'Can only create a instance with a image in the following state: {0}'.format( ResourceState.Created.value)) flavor: Flavor = Flavor.get(request.flavor_name) if flavor is None: raise cherrypy.HTTPError(404, 'A flavor with the requested name does not exist.') keypairs = [] for keypair_name in request.keypair_names: keypair = Keypair.get(project, keypair_name) if keypair is None: raise cherrypy.HTTPError(404, 'A keypair with the requested name of {0} does not exist.'.format( keypair_name)) keypairs.append(keypair) # TODO: User inputs service account email instead of id # Only project service accounts are allowed if request.service_account_name is not None: service_account = ProjectServiceAccount.get(project, request.service_account_name) if service_account is None: raise cherrypy.HTTPError(404, 'A service account with the requested name of {0} does not exist.'.format( request.service_account_name)) else: service_account = ProjectServiceAccount.get(project, 'default') if service_account is None: raise cherrypy.HTTPError(500, 'Could not find a default service account to attach to the instance.') quota: ProjectQuota = ProjectQuota.get(project.name) used_vcpu = quota.used_vcpu + flavor.vcpus used_ram = quota.used_ram + flavor.ram requested_disk = flavor.disk if request.disk is not None: requested_disk = request.disk used_disk = quota.used_disk + requested_disk if quota.vcpu != -1: if used_vcpu > quota.vcpu: raise QuotaError("VCPU", flavor.vcpus, quota.used_vcpu, quota.vcpu) if quota.ram != -1: if used_ram > quota.ram: raise QuotaError("Ram", flavor.ram, quota.used_ram, quota.ram) if quota.disk != -1: if used_disk > quota.disk: raise QuotaError("Disk", requested_disk, quota.used_disk, quota.disk) quota.used_vcpu = used_vcpu quota.used_ram = used_ram quota.used_disk = used_disk quota.save() network_port = NetworkPort() network_port.name = str(uuid.uuid4()) # We don't care about the network port's name, just that it's unique network_port.project = project network_port.network = network network_port.create() instance = Instance() instance.name = request.name instance.project = project instance.region = region if zone is not None: instance.zone = zone instance.image = image instance.service_account = service_account instance.network_port = network_port instance.keypairs = keypairs if request.user_data is not None: if len(request.user_data) > 0: instance.user_data = request.user_data for k, v in request.tags.items(): instance.add_tag(k, v) instance.flavor = flavor if request.disk is not None: instance.disk = request.disk if request.initial_volumes is not None: initial_volumes = [] for initial_volume in request.initial_volumes: initial_volumes.append(initial_volume.to_native()) instance.initial_volumes = initial_volumes instance.create() return ResponseInstance.from_database(instance)
def created(self, model: Zone): region = model.region if region.state == ResourceState.Deleting: model.delete() return