diff --git a/pkg/apis/v1alpha/install.go b/pkg/apis/v1alpha/install.go index f120040a2..772dc7e9f 100644 --- a/pkg/apis/v1alpha/install.go +++ b/pkg/apis/v1alpha/install.go @@ -17,25 +17,26 @@ limitations under the License. package v1alpha import ( - "github.com/emicklei/go-restful" - "kubesphere.io/kubesphere/pkg/apis/v1alpha/nodes" - "kubesphere.io/kubesphere/pkg/apis/v1alpha/kubeconfig" - "kubesphere.io/kubesphere/pkg/apis/v1alpha/kubectl" - "kubesphere.io/kubesphere/pkg/apis/v1alpha/registries" + "github.com/emicklei/go-restful" + "kubesphere.io/kubesphere/pkg/apis/v1alpha/kubeconfig" + "kubesphere.io/kubesphere/pkg/apis/v1alpha/kubectl" + "kubesphere.io/kubesphere/pkg/apis/v1alpha/nodes" + "kubesphere.io/kubesphere/pkg/apis/v1alpha/registries" + "kubesphere.io/kubesphere/pkg/apis/v1alpha/storage" ) func init() { - ws := new(restful.WebService) - ws.Path("/api/v1alpha") + ws := new(restful.WebService) + ws.Path("/api/v1alpha") - nodes.Register(ws,"/nodes") - kubeconfig.Register(ws, "/namespaces/{namespace}/kubeconfig") - kubectl.Register(ws, "/namespaces/{namespace}/kubectl") - registries.Register(ws,"/registries") + nodes.Register(ws, "/nodes") + kubeconfig.Register(ws, "/namespaces/{namespace}/kubeconfig") + kubectl.Register(ws, "/namespaces/{namespace}/kubectl") + registries.Register(ws, "/registries") + storage.Register(ws, "/storage") - // add webservice to default container - restful.Add(ws) + // add webservice to default container + restful.Add(ws) } - diff --git a/pkg/apis/v1alpha/storage/storage_handler.go b/pkg/apis/v1alpha/storage/storage_handler.go new file mode 100644 index 000000000..0c935e881 --- /dev/null +++ b/pkg/apis/v1alpha/storage/storage_handler.go @@ -0,0 +1,19 @@ +package storage + +import ( + "github.com/emicklei/go-restful" + "kubesphere.io/kubesphere/pkg/filter/route" + "kubesphere.io/kubesphere/pkg/models" +) + +func Register(ws *restful.WebService, subPath string) { + ws.Route(ws.GET(subPath+"/storageclasses/{storageclass}/persistentvolumeclaims"). + To(models.GetPvcListBySc).Filter(route.RouteLogging)). + Consumes(restful.MIME_JSON, restful.MIME_XML). + Produces(restful.MIME_JSON) + + ws.Route(ws.GET(subPath+"/storageclasses/{storageclass}/metrics"). + To(models.GetScMetrics).Filter(route.RouteLogging)). + Consumes(restful.MIME_JSON, restful.MIME_XML). + Produces(restful.MIME_JSON) +} diff --git a/pkg/models/storage.go b/pkg/models/storage.go new file mode 100644 index 000000000..2e58dce98 --- /dev/null +++ b/pkg/models/storage.go @@ -0,0 +1,97 @@ +package models + +import ( + "github.com/emicklei/go-restful" + "github.com/golang/glog" + v12 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" + "k8s.io/apimachinery/pkg/apis/meta/v1" + "kubesphere.io/kubesphere/pkg/client" + "kubesphere.io/kubesphere/pkg/constants" + "net/http" +) + +type pvcListBySc struct { + Name string `json:"name"` + Claims []v12.PersistentVolumeClaim `json:"persistentvolumeclaims"` +} + +type scMetrics struct { + Name string `json:"name"` + Metrics storageMetrics `json:"metrics"` +} + +type storageMetrics struct { + Capacity string `json:"capacity,omitempty"` + Usage string `json:"usage,omitempty"` +} + +// List all PersistentVolumeClaims of a specific StorageClass +// Extended API URL: "GET /api/v1alpha/storage/storageclasses/{name}/persistentvolumeclaims" +func GetPvcListBySc(request *restful.Request, response *restful.Response) { + + scName := request.PathParameter("storageclass") + glog.Infof("Run GetPvcListBySc: SC = %s", scName) + claims, err := getPvcListBySc(scName) + if err != nil { + response.WriteError(http.StatusInternalServerError, err) + } + result := constants.ResultMessage{ + Kind: constants.KIND, + ApiVersion: constants.APIVERSION, + Data: pvcListBySc{scName, claims}} + + response.WriteAsJson(result) +} + +// Get metrics of a specific StorageClass +// Extended API URL: "GET /api/v1alpha/storage/storageclasses/{name}/metrics" +func GetScMetrics(request *restful.Request, response *restful.Response) { + scName := request.PathParameter("storageclass") + glog.Infof("Run GetPvcListBySc: SC = %s", scName) + + metrics, err := getScMetrics(scName) + if err != nil { + response.WriteError(http.StatusInternalServerError, err) + } + result := constants.ResultMessage{ + Kind: constants.KIND, + ApiVersion: constants.APIVERSION, + Data: scMetrics{Name: scName, Metrics: metrics}, + } + response.WriteAsJson(result) +} + +func getPvcListBySc(storageclass string) (res []v12.PersistentVolumeClaim, err error) { + + cli := client.NewK8sClient() + claimList, err := cli.CoreV1().PersistentVolumeClaims("").List(v1.ListOptions{}) + if err != nil { + return nil, err + } + + for _, claim := range claimList.Items { + if *claim.Spec.StorageClassName != storageclass { + continue + } + res = append(res, claim) + } + return res, nil +} + +func getScMetrics(storageclass string) (res storageMetrics, err error) { + cli := client.NewK8sClient() + pvList, err := cli.CoreV1().PersistentVolumes().List(v1.ListOptions{}) + if err != nil { + return storageMetrics{}, err + } + + var total resource.Quantity + for _, volume := range pvList.Items { + if volume.Spec.StorageClassName != storageclass { + continue + } + total.Add(volume.Spec.Capacity[v12.ResourceStorage]) + } + return storageMetrics{Usage: total.String()}, nil +}