Exemple #1
0
    def created(self, model: ProjectQuota):
        used_vcpu = 0
        used_ram = 0
        used_disk = 0

        project = Project.get(model.name)
        if project is None:
            model.delete()
            return

        instances = Instance.list(project)
        for instance in instances:
            used_vcpu += instance.vcpus
            used_ram += instance.ram
            used_disk += instance.disk

        volumes = Volume.list(project)
        for volume in volumes:
            used_disk += volume.size

        if used_vcpu != model.used_vcpu or used_ram != model.used_ram or used_disk != model.used_disk:
            model.used_vcpu = used_vcpu
            model.used_ram = used_ram
            model.used_disk = used_disk
            model.save(ignore=True)
Exemple #2
0
    def action_clone(self, **_):
        """Clone a volume
        ---
        put:
            description: Clone a volume
            tags:
                - compute
                - volume
            requestBody:
                description: Clone options
            responses:
                200:
                    description: The created volume
        """
        request: RequestCloneVolume = cherrypy.request.model
        project: Project = cherrypy.request.project
        volume: Volume = cherrypy.request.resource_object
        if volume.state != ResourceState.Created:
            raise cherrypy.HTTPError(
                400, 'Volume is not in the following state: ' +
                ResourceState.Created.value)
        if volume.task is not None:
            raise cherrypy.HTTPError(
                400, "Please wait for the current task to finish.")
        if volume.attached_to_name is not None:
            raise cherrypy.HTTPError(
                409, 'Cannot clone while attached to an instance')
        if Volume.get(cherrypy.request.project, request.name) is not None:
            raise cherrypy.HTTPError(
                409, 'A volume with the requested name already exists.')

        quota: ProjectQuota = ProjectQuota.get(project.name)
        used_disk = quota.used_disk + volume.size
        if quota.disk != -1:
            if used_disk > quota.disk:
                raise QuotaError("Disk", volume.size, quota.used_disk,
                                 quota.disk)

        quota.used_disk = used_disk
        quota.save()

        new_volume = Volume()
        new_volume.project = volume.project
        new_volume.name = request.name
        new_volume.zone = volume.zone
        new_volume.size = volume.size
        new_volume.cloned_from = volume
        new_volume.create()

        volume.task = VolumeTask.CLONING
        volume.task_kwargs = {'volume_name': str(new_volume.name)}
        volume.save()

        return ResponseVolume.from_database(new_volume)
Exemple #3
0
    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)
Exemple #4
0
    def create(self):
        """Create a project
        ---
        post:
            description: Create a project
            tags:
                - iam
                - project
            requestBody:
                description: Project to create
            responses:
                200:
                    description: The created project
        """
        request: RequestCreateProject = cherrypy.request.model

        project = Project.get(request.name)
        if project is not None:
            raise cherrypy.HTTPError(409, 'A project with the requested name already exists.')

        if request.name == "system":
            raise cherrypy.HTTPError(409, 'Cannot use a reserved name as the project name')

        project = Project()
        project.name = request.name
        project.create()

        IAMProjectRole.create_default_roles(project)
        ProjectServiceAccount.create_default_service_account(project)

        quota = ProjectQuota()
        quota.name = project.name
        quota.create()

        IAMPolicy.create_project_policy(project, cherrypy.request.token)

        return ResponseProject.from_database(project)
Exemple #5
0
 def get(self):
     """Get a project's quota
     ---
     get:
         description: Get a project's quota
         tags:
             - iam
             - project
         responses:
             200:
                 description: The project's quota
     """
     project: Project = cherrypy.request.project
     quota = ProjectQuota.get(project.name)
     return ResponseProjectQuota.from_database(quota)
Exemple #6
0
    def action_grow(self, **_):
        """Grow a volume
        ---
        put:
            description: Grow a volume
            tags:
                - compute
                - volume
            requestBody:
                description: Grow options
            responses:
                204:
                    description: Volume growing
        """
        project: Project = cherrypy.request.project
        request: RequestGrowVolume = cherrypy.request.model
        cherrypy.response.status = 204

        volume: Volume = cherrypy.request.resource_object
        if volume.state != ResourceState.Created:
            raise cherrypy.HTTPError(
                400, 'Volume is not in the following state: ' +
                ResourceState.Created.value)
        if volume.task is not None:
            raise cherrypy.HTTPError(
                400, "Please wait for the current task to finish.")

        if volume.attached_to_name is not None:
            raise cherrypy.HTTPError(
                409, 'Cannot grow while attached to an instance')

        if request.size <= volume.size:
            raise cherrypy.HTTPError(
                400, 'Size must be bigger than the current volume size.')

        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.task = VolumeTask.GROWING
        volume.task_kwargs = {"size": request.size}
        volume.save()
Exemple #7
0
    def modify(self):
        """Modify a project's quota
        ---
        post:
            description: Modify a project's quota
            tags:
                - iam
                - project
            requestBody:
                description: Quota options
            responses:
                204:
                    description: Quota set
        """
        cherrypy.response.status = 204
        request: RequestProjectModifyQuota = cherrypy.request.model
        project: Project = cherrypy.request.project
        quota: ProjectQuota = ProjectQuota.get(project.name)

        quota.vcpu = request.vcpu
        quota.ram = request.ram
        quota.disk = request.disk
        quota.save()
Exemple #8
0
 def deleted(self, model: ProjectQuota):
     model.delete(force=True)
Exemple #9
0
    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
Exemple #10
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)