From f1fec1b49e2d57456f14a0f7f6db4ceec037a4e2 Mon Sep 17 00:00:00 2001 From: Zexi Li Date: Thu, 13 Sep 2018 15:04:39 +0800 Subject: [PATCH 1/9] climc: k8s options, code refactor --- cmd/climc/shell/k8s/charts.go | 45 +---- cmd/climc/shell/k8s/clusters.go | 178 +++---------------- cmd/climc/shell/k8s/configmap.go | 21 +-- cmd/climc/shell/k8s/cronjob.go | 9 + cmd/climc/shell/k8s/deployment.go | 209 ++++------------------- cmd/climc/shell/k8s/ingress.go | 33 +--- cmd/climc/shell/k8s/job.go | 9 + cmd/climc/shell/k8s/k8s.go | 197 +++++++++++++-------- cmd/climc/shell/k8s/k8s_node.go | 34 +--- cmd/climc/shell/k8s/namespace.go | 34 +--- cmd/climc/shell/k8s/nodes.go | 92 ++-------- cmd/climc/shell/k8s/pods.go | 48 +----- cmd/climc/shell/k8s/pv.go | 9 + cmd/climc/shell/k8s/pvc.go | 9 + cmd/climc/shell/k8s/raw.go | 3 +- cmd/climc/shell/k8s/releases.go | 137 ++------------- cmd/climc/shell/k8s/repos.go | 76 ++------- cmd/climc/shell/k8s/secret.go | 9 + cmd/climc/shell/k8s/service.go | 21 +-- cmd/climc/shell/k8s/statefulset.go | 33 +--- cmd/climc/shell/k8s/storageclass.go | 9 + cmd/climc/shell/k8s/tiller.go | 40 +---- pkg/mcclient/modules/k8s/cronjob.go | 19 +++ pkg/mcclient/modules/k8s/job.go | 19 +++ pkg/mcclient/modules/k8s/pv.go | 38 +++++ pkg/mcclient/modules/k8s/pvc.go | 34 ++++ pkg/mcclient/modules/k8s/secret.go | 25 +++ pkg/mcclient/modules/k8s/storageclass.go | 26 +++ pkg/mcclient/options/k8s/base.go | 104 +++++++++++ pkg/mcclient/options/k8s/charts.go | 49 ++++++ pkg/mcclient/options/k8s/cluster.go | 177 +++++++++++++++++++ pkg/mcclient/options/k8s/deployment.go | 147 ++++++++++++++++ pkg/mcclient/options/k8s/node.go | 69 ++++++++ pkg/mcclient/options/k8s/release.go | 136 +++++++++++++++ pkg/mcclient/options/k8s/repo.go | 44 +++++ pkg/mcclient/options/k8s/tiller.go | 44 +++++ 36 files changed, 1209 insertions(+), 977 deletions(-) create mode 100644 cmd/climc/shell/k8s/cronjob.go create mode 100644 cmd/climc/shell/k8s/job.go create mode 100644 cmd/climc/shell/k8s/pv.go create mode 100644 cmd/climc/shell/k8s/pvc.go create mode 100644 cmd/climc/shell/k8s/secret.go create mode 100644 cmd/climc/shell/k8s/storageclass.go create mode 100644 pkg/mcclient/modules/k8s/cronjob.go create mode 100644 pkg/mcclient/modules/k8s/job.go create mode 100644 pkg/mcclient/modules/k8s/pv.go create mode 100644 pkg/mcclient/modules/k8s/pvc.go create mode 100644 pkg/mcclient/modules/k8s/secret.go create mode 100644 pkg/mcclient/modules/k8s/storageclass.go create mode 100644 pkg/mcclient/options/k8s/base.go create mode 100644 pkg/mcclient/options/k8s/charts.go create mode 100644 pkg/mcclient/options/k8s/cluster.go create mode 100644 pkg/mcclient/options/k8s/deployment.go create mode 100644 pkg/mcclient/options/k8s/node.go create mode 100644 pkg/mcclient/options/k8s/release.go create mode 100644 pkg/mcclient/options/k8s/repo.go create mode 100644 pkg/mcclient/options/k8s/tiller.go diff --git a/cmd/climc/shell/k8s/charts.go b/cmd/climc/shell/k8s/charts.go index 1eaf2c7b04..487cbbbd73 100644 --- a/cmd/climc/shell/k8s/charts.go +++ b/cmd/climc/shell/k8s/charts.go @@ -1,10 +1,9 @@ package k8s import ( - json "yunion.io/x/jsonutils" - "yunion.io/x/onecloud/pkg/mcclient" "yunion.io/x/onecloud/pkg/mcclient/modules/k8s" + o "yunion.io/x/onecloud/pkg/mcclient/options/k8s" ) func initChart() { @@ -12,32 +11,8 @@ func initChart() { return resourceCmdN("chart", suffix) } - type listOpt struct { - baseListOptions - Name string `help:"Chart name"` - Repo string `help:"Repository name"` - RepoUrl string `help:"Repository url"` - AllVersion bool `json:"Get Chart all history versions"` - Keyword string `json:"Chart keyword"` - } - R(&listOpt{}, cmdN("list"), "List k8s helm global charts", func(s *mcclient.ClientSession, args *listOpt) error { - params := fetchPagingParams(args.baseListOptions) - if len(args.Name) != 0 { - params.Add(json.NewString(args.Name), "name") - } - if len(args.Repo) != 0 { - params.Add(json.NewString(args.Repo), "repo") - } - if len(args.RepoUrl) != 0 { - params.Add(json.NewString(args.RepoUrl), "repo_url") - } - if args.AllVersion { - params.Add(json.JSONTrue, "all_version") - } - if len(args.Keyword) != 0 { - params.Add(json.NewString(args.Keyword), "keyword") - } - charts, err := k8s.Charts.List(s, params) + R(&o.ChartListOptions{}, cmdN("list"), "List k8s helm global charts", func(s *mcclient.ClientSession, args *o.ChartListOptions) error { + charts, err := k8s.Charts.List(s, args.Params()) if err != nil { return err } @@ -46,18 +21,8 @@ func initChart() { return nil }) - type getOpt struct { - REPO string `help:"Repo of the chart"` - NAME string `help:"Chart name"` - Version string `help:"Chart version"` - } - R(&getOpt{}, cmdN("show"), "Show details of a chart", func(s *mcclient.ClientSession, args *getOpt) error { - params := json.NewDict() - params.Add(json.NewString(args.REPO), "repo") - if args.Version != "" { - params.Add(json.NewString(args.Version), "version") - } - chart, err := k8s.Charts.Get(s, args.NAME, params) + R(&o.ChartGetOptions{}, cmdN("show"), "Show details of a chart", func(s *mcclient.ClientSession, args *o.ChartGetOptions) error { + chart, err := k8s.Charts.Get(s, args.NAME, args.Params()) if err != nil { return err } diff --git a/cmd/climc/shell/k8s/clusters.go b/cmd/climc/shell/k8s/clusters.go index aa95a2d594..05a09d7fef 100644 --- a/cmd/climc/shell/k8s/clusters.go +++ b/cmd/climc/shell/k8s/clusters.go @@ -2,35 +2,19 @@ package k8s import ( "fmt" - "io/ioutil" - "strings" - "yunion.io/x/jsonutils" "yunion.io/x/onecloud/pkg/mcclient" "yunion.io/x/onecloud/pkg/mcclient/modules/k8s" - "yunion.io/x/onecloud/pkg/mcclient/options" - "yunion.io/x/pkg/util/sets" + o "yunion.io/x/onecloud/pkg/mcclient/options/k8s" ) func initCluster() { cmdN := func(suffix string) string { return kubeResourceCmdN("cluster", suffix) } - type listOpt struct { - options.BaseListOptions - } - R(&listOpt{}, cmdN("list"), "List k8s infra clusters", func(s *mcclient.ClientSession, args *listOpt) error { - args.Details = options.Bool(true) - var params *jsonutils.JSONDict - { - var err error - params, err = args.BaseListOptions.Params() - if err != nil { - return err - } - } - result, err := k8s.Clusters.List(s, params) + R(&o.ClusterListOptions{}, cmdN("list"), "List k8s infra clusters", func(s *mcclient.ClientSession, args *o.ClusterListOptions) error { + result, err := k8s.Clusters.List(s, args.Params()) if err != nil { return err } @@ -38,33 +22,8 @@ func initCluster() { return nil }) - type createOpt struct { - NAME string `help:"Name of cluster"` - Mode string `help:"Cluster mode" choices:"internal"` - K8sVersion string `help:"Cluster kubernetes components version" choices:"v1.8.10|v1.9.5|v1.10.0"` - InfraImage string `help:"Cluster kubelet infra container image"` - Cidr string `help:"Cluster service CIDR, e.g. 10.43.0.0/16"` - Domain string `help:"Cluster pod domain, e.g. cluster.local"` - } - R(&createOpt{}, cmdN("create"), "Create k8s cluster", func(s *mcclient.ClientSession, args *createOpt) error { - params := jsonutils.NewDict() - params.Add(jsonutils.NewString(args.NAME), "name") - if args.Mode != "" { - params.Add(jsonutils.NewString(args.Mode), "mode") - } - if args.K8sVersion != "" { - params.Add(jsonutils.NewString(args.K8sVersion), "k8s_version") - } - if args.InfraImage != "" { - params.Add(jsonutils.NewString(args.InfraImage), "infra_container_image") - } - if args.Cidr != "" { - params.Add(jsonutils.NewString(args.Cidr), "cluster_cidr") - } - if args.Domain != "" { - params.Add(jsonutils.NewString(args.Domain), "cluster_domain") - } - cluster, err := k8s.Clusters.Create(s, params) + R(&o.ClusterCreateOptions{}, cmdN("create"), "Create k8s cluster", func(s *mcclient.ClientSession, args *o.ClusterCreateOptions) error { + cluster, err := k8s.Clusters.Create(s, args.Params()) if err != nil { return err } @@ -72,21 +31,12 @@ func initCluster() { return nil }) - type importOpt struct { - NAME string `help:"Name of cluster to import"` - Kubeconfig string `help:"Kubernetes auth config"` - } - R(&importOpt{}, cmdN("import"), "Import exists YKE deployed kubernetes cluster", func(s *mcclient.ClientSession, args *importOpt) error { - if args.Kubeconfig == "" { - return fmt.Errorf("Kubeconfig file must provide") - } - kubeconfig, err := ioutil.ReadFile(args.Kubeconfig) + R(&o.ClusterImportOptions{}, cmdN("import"), "Import exists YKE deployed kubernetes cluster", func(s *mcclient.ClientSession, args *o.ClusterImportOptions) error { + params, err := args.Params() if err != nil { - return fmt.Errorf("Read kube config %q error: %v", args.Kubeconfig, err) + return err } - params := jsonutils.NewDict() - params.Add(jsonutils.NewString(string(kubeconfig)), "kube_config") cluster, err := k8s.Clusters.PerformAction(s, args.NAME, "import", params) if err != nil { return err @@ -95,16 +45,8 @@ func initCluster() { return nil }) - type updateOpt struct { - NAME string `help:"Name of cluster"` - K8sVersion string `help:"Cluster kubernetes components version" choices:"v1.8.10|v1.9.5|v1.10.0" default:"v1.9.5"` - } - R(&updateOpt{}, cmdN("update"), "Update k8s cluster", func(s *mcclient.ClientSession, args *updateOpt) error { - params := jsonutils.NewDict() - if args.K8sVersion != "" { - params.Add(jsonutils.NewString(args.K8sVersion), "k8s_version") - } - cluster, err := k8s.Clusters.Update(s, args.NAME, params) + R(&o.ClusterUpdateOptions{}, cmdN("update"), "Update k8s cluster", func(s *mcclient.ClientSession, args *o.ClusterUpdateOptions) error { + cluster, err := k8s.Clusters.Update(s, args.NAME, args.Params()) if err != nil { return err } @@ -112,19 +54,8 @@ func initCluster() { return nil }) - type identOpt struct { - ID string `help:"ID or name of the cluster"` - } - type deployOpt struct { - identOpt - Force bool `help:"Force deploy"` - } - R(&deployOpt{}, cmdN("deploy"), "Start deploy a cluster", func(s *mcclient.ClientSession, args *deployOpt) error { - params := jsonutils.NewDict() - if args.Force { - params.Add(jsonutils.JSONTrue, "force") - } - ret, err := k8s.Clusters.PerformAction(s, args.ID, "deploy", params) + R(&o.ClusterDeployOptions{}, cmdN("deploy"), "Start deploy a cluster", func(s *mcclient.ClientSession, args *o.ClusterDeployOptions) error { + ret, err := k8s.Clusters.PerformAction(s, args.ID, "deploy", args.Params()) if err != nil { return err } @@ -132,14 +63,7 @@ func initCluster() { return nil }) - type identsOpt struct { - ID []string `help:"ID of clusters to operate"` - } - - type getOpt struct { - identOpt - } - R(&getOpt{}, cmdN("show"), "Show details of a cluster", func(s *mcclient.ClientSession, args *getOpt) error { + R(&o.IdentOptions{}, cmdN("show"), "Show details of a cluster", func(s *mcclient.ClientSession, args *o.IdentOptions) error { result, err := k8s.Clusters.Get(s, args.ID, nil) if err != nil { return err @@ -148,25 +72,14 @@ func initCluster() { return nil }) - type deleteOpt struct { - identsOpt - } - R(&deleteOpt{}, cmdN("delete"), "Delete cluster", func(s *mcclient.ClientSession, args *deleteOpt) error { + R(&o.ClusterDeleteOptions{}, cmdN("delete"), "Delete cluster", func(s *mcclient.ClientSession, args *o.ClusterDeleteOptions) error { ret := k8s.Clusters.BatchDeleteWithParam(s, args.ID, nil, nil) printBatchResults(ret, k8s.Clusters.GetColumns(s)) return nil }) - type kubeConfigOpt struct { - getOpt - Directly bool `help:"Get directly connect kubeconfig"` - } - R(&kubeConfigOpt{}, cmdN("kubeconfig"), "Generate kubeconfig of a cluster", func(s *mcclient.ClientSession, args *kubeConfigOpt) error { - params := jsonutils.NewDict() - if args.Directly { - params.Add(jsonutils.JSONTrue, "directly") - } - ret, err := k8s.Clusters.PerformAction(s, args.ID, "generate-kubeconfig", params) + R(&o.ClusterKubeconfigOptions{}, cmdN("kubeconfig"), "Generate kubeconfig of a cluster", func(s *mcclient.ClientSession, args *o.ClusterKubeconfigOptions) error { + ret, err := k8s.Clusters.PerformAction(s, args.ID, "generate-kubeconfig", args.Params()) if err != nil { return err } @@ -178,7 +91,7 @@ func initCluster() { return nil }) - R(&getOpt{}, cmdN("engineconfig"), "Get kubernetes engine config of a cluster", func(s *mcclient.ClientSession, args *getOpt) error { + R(&o.IdentOptions{}, cmdN("engineconfig"), "Get kubernetes engine config of a cluster", func(s *mcclient.ClientSession, args *o.IdentOptions) error { ret, err := k8s.Clusters.GetSpecific(s, args.ID, "engine-config", nil) if err != nil { return err @@ -191,7 +104,7 @@ func initCluster() { return nil }) - R(&getOpt{}, cmdN("webhookauthurl"), "Get cluster kubernetes api server webhook auth url", func(s *mcclient.ClientSession, args *getOpt) error { + R(&o.IdentOptions{}, cmdN("webhookauthurl"), "Get cluster kubernetes api server webhook auth url", func(s *mcclient.ClientSession, args *o.IdentOptions) error { ret, err := k8s.Clusters.GetSpecific(s, args.ID, "webhook-auth-url", nil) if err != nil { return err @@ -204,25 +117,11 @@ func initCluster() { return nil }) - type addNodesOpt struct { - identOpt - NodeConfig []string `help:"Node spec, 'host:[roles]' e.g: --node-config host01:controlplane,etcd,worker --node-config host02:worker"` - AutoDeploy bool `help:"Auto deploy"` - } - R(&addNodesOpt{}, cmdN("addnodes"), "Add nodes to cluster", func(s *mcclient.ClientSession, args *addNodesOpt) error { - params := jsonutils.NewDict() - if args.AutoDeploy { - params.Add(jsonutils.JSONTrue, "auto_deploy") + R(&o.ClusterAddNodesOptions{}, cmdN("addnodes"), "Add nodes to cluster", func(s *mcclient.ClientSession, args *o.ClusterAddNodesOptions) error { + params, err := args.Params() + if err != nil { + return err } - nodesArray := jsonutils.NewArray() - for _, config := range args.NodeConfig { - opt, err := parseNodeAddConfigStr(config) - if err != nil { - return err - } - nodesArray.Add(jsonutils.Marshal(opt)) - } - params.Add(nodesArray, "nodes") ret, err := k8s.Clusters.PerformAction(s, args.ID, "add-nodes", params) if err != nil { return err @@ -231,36 +130,3 @@ func initCluster() { return nil }) } - -type dockerConfig struct { - RegistryMirrors []string `json:"registry-mirrors"` - InsecureRegistries []string `json:"insecure-registries"` -} - -type nodeAddConfig struct { - Host string `json:"host"` - Roles []string `json:"roles"` - Name string `json:"name"` - HostnameOverride string `json:"hostname_override"` - DockerdConfig dockerConfig `json:"dockerd_config"` -} - -func parseNodeAddConfigStr(config string) (nodeAddConfig, error) { - ret := nodeAddConfig{} - parts := strings.Split(config, ":") - if len(parts) != 2 { - return ret, fmt.Errorf("Invalid config: %q", config) - } - host := parts[0] - roleStr := parts[1] - ret.Host = host - roles := []string{} - for _, role := range strings.Split(roleStr, ",") { - if !sets.NewString("etcd", "controlplane", "worker").Has(role) { - return ret, fmt.Errorf("Invalid role: %q", role) - } - roles = append(roles, role) - } - ret.Roles = roles - return ret, nil -} diff --git a/cmd/climc/shell/k8s/configmap.go b/cmd/climc/shell/k8s/configmap.go index 6162aa1dc4..a4931fd9b5 100644 --- a/cmd/climc/shell/k8s/configmap.go +++ b/cmd/climc/shell/k8s/configmap.go @@ -1,28 +1,9 @@ package k8s import ( - "yunion.io/x/onecloud/pkg/mcclient" "yunion.io/x/onecloud/pkg/mcclient/modules/k8s" ) func initConfigMap() { - cmdN := func(suffix string) string { - return resourceCmdN("configmap", suffix) - } - - type listOpt struct { - namespaceListOptions - baseListOptions - } - R(&listOpt{}, cmdN("list"), "List k8s configmap", func(s *mcclient.ClientSession, args *listOpt) error { - params := fetchNamespaceParams(args.namespaceListOptions) - params.Update(fetchPagingParams(args.baseListOptions)) - params.Update(args.ClusterParams()) - ret, err := k8s.ConfigMaps.List(s, params) - if err != nil { - return err - } - printList(ret, k8s.ConfigMaps.GetColumns(s)) - return nil - }) + initK8sNamespaceResource("configmap", k8s.ConfigMaps) } diff --git a/cmd/climc/shell/k8s/cronjob.go b/cmd/climc/shell/k8s/cronjob.go new file mode 100644 index 0000000000..44c912aeb8 --- /dev/null +++ b/cmd/climc/shell/k8s/cronjob.go @@ -0,0 +1,9 @@ +package k8s + +import ( + "yunion.io/x/onecloud/pkg/mcclient/modules/k8s" +) + +func initCronJob() { + initK8sNamespaceResource("cronjob", k8s.CronJobs) +} diff --git a/cmd/climc/shell/k8s/deployment.go b/cmd/climc/shell/k8s/deployment.go index 21e8338e7d..bd1934cedd 100644 --- a/cmd/climc/shell/k8s/deployment.go +++ b/cmd/climc/shell/k8s/deployment.go @@ -1,199 +1,48 @@ package k8s import ( - "fmt" - "io/ioutil" - "strconv" - "strings" - - "yunion.io/x/jsonutils" - "yunion.io/x/pkg/util/regutils" - "yunion.io/x/pkg/util/sets" - "yunion.io/x/onecloud/pkg/mcclient" "yunion.io/x/onecloud/pkg/mcclient/modules/k8s" + o "yunion.io/x/onecloud/pkg/mcclient/options/k8s" ) func initDeployment() { - cmdN := func(suffix string) string { - return resourceCmdN("deployment", suffix) - } + cmd := initK8sNamespaceResource("deployment", k8s.Deployments) + cmdN := cmd.CommandNameFactory - R(&NamespaceResourceListOptions{}, cmdN("list"), "List k8s deployment", func(s *mcclient.ClientSession, args *NamespaceResourceListOptions) error { - ret, err := k8s.Deployments.List(s, args.Params()) - if err != nil { - return err - } - PrintListResultTable(ret, k8s.Deployments, s) - return nil - }) - - type createOpt struct { - namespaceOptions - NAME string `help:"Name of deployment"` - Image string `help:"The image for the container to run"` - Replicas int64 `help:"Number of replicas for pods in this deployment"` - RunAsPrivileged bool `help:"Whether to run the container as privileged user"` - Labels string `help:"Comma separated labels to apply to the pod(s)"` - Env []string `help:"Environment variables to set in container"` - Port []string `help:"Port for the service that is created, format is :: e.g. tcp:80:3000"` - Net string `help:"Network config, e.g. net1, net1:10.168.222.171"` - } - R(&createOpt{}, cmdN("create"), "Create deployment resource", func(s *mcclient.ClientSession, args *createOpt) error { - params := args.ClusterParams() - if len(args.Image) == 0 { - return fmt.Errorf("Image must provided") - } - params.Add(jsonutils.NewString(args.NAME), "name") - params.Add(jsonutils.NewString(args.Image), "containerImage") - if args.Namespace != "" { - params.Add(jsonutils.NewString(args.Namespace), "namespace") - } - if args.Replicas > 1 { - params.Add(jsonutils.NewInt(args.Replicas), "replicas") - } - if args.RunAsPrivileged { - params.Add(jsonutils.JSONTrue, "runAsPrivileged") - } - if len(args.Port) != 0 { - portMappings, err := parsePortMappings(args.Port) + createCmd := NewCommand( + &o.DeploymentCreateOptions{}, + cmdN("create"), + "Create deployment resource", + func(s *mcclient.ClientSession, args *o.DeploymentCreateOptions) error { + params, err := args.Params() if err != nil { return err } - params.Add(portMappings, "portMappings") - } - - envList := jsonutils.NewArray() - for _, env := range args.Env { - parts := strings.Split(env, "=") - if len(parts) != 2 { - return fmt.Errorf("Bad env value: %v", env) - } - envObj := jsonutils.NewDict() - envObj.Add(jsonutils.NewString(parts[0]), "name") - envObj.Add(jsonutils.NewString(parts[1]), "value") - envList.Add(envObj) - } - params.Add(envList, "variables") - if args.Net != "" { - net, err := parseNetConfig(args.Net) + ret, err := k8s.Deployments.Create(s, params) if err != nil { return err } - params.Add(net, "networkConfig") - } - ret, err := k8s.Deployments.Create(s, params) - if err != nil { - return err - } - printObject(ret) - return nil - }) + printObject(ret) + return nil + }) - type getOpt struct { - resourceGetOptions - } - R(&getOpt{}, cmdN("show"), "Get deployment details", func(s *mcclient.ClientSession, args *getOpt) error { - id := args.NAME - params := args.ClusterParams() - if args.Namespace != "" { - params.Add(jsonutils.NewString(args.Namespace), "namespace") - } - ret, err := k8s.Deployments.Get(s, id, params) - if err != nil { - return err - } - printObjectYAML(ret) - return nil - }) - - type createFromFileOpt struct { - resourceGetOptions - FILE string `help:"K8s resource YAML or JSON file"` - } - R(&createFromFileOpt{}, "k8s-create", "Create resource by file", func(s *mcclient.ClientSession, args *createFromFileOpt) error { - params := args.ClusterParams() - params.Add(jsonutils.NewString(args.NAME), "name") - - content, err := ioutil.ReadFile(args.FILE) - if err != nil { - return err - } - namespace := args.Namespace - if namespace != "" { - params.Add(jsonutils.NewString(namespace), "namespace") - } - params.Add(jsonutils.NewString(string(content)), "content") - ret, err := k8s.DeployFromFile.Create(s, params) - if err != nil { - return err - } - printObjectYAML(ret) - return nil - }) -} - -type portMapping struct { - Port int32 `json:"port"` - TargetPort int32 `json:"targetPort"` - Protocol string `json:"protocol"` -} - -func parsePortMapping(port string) (*portMapping, error) { - if len(port) == 0 { - return nil, fmt.Errorf("empty port mapping desc string") - } - parts := strings.Split(port, ":") - mapping := &portMapping{} - for _, part := range parts { - if sets.NewString("tcp", "udp").Has(strings.ToLower(part)) { - mapping.Protocol = strings.ToUpper(part) - } - if port, err := strconv.Atoi(part); err != nil { - continue - } else { - if mapping.Port == 0 { - mapping.Port = int32(port) - } else { - mapping.TargetPort = int32(port) + createFromFileCmd := NewCommand( + &o.DeploymentCreateFromFileOptions{}, + "k8s-create", + "Create resource by file", + func(s *mcclient.ClientSession, args *o.DeploymentCreateFromFileOptions) error { + params, err := args.Params() + if err != nil { + return err } - } - } - if mapping.Protocol == "" { - mapping.Protocol = "TCP" - } - if mapping.Port <= 0 { - return nil, fmt.Errorf("Service port not provided") - } - if mapping.TargetPort < 0 { - return nil, fmt.Errorf("Container invalid targetPort %d", mapping.TargetPort) - } - if mapping.TargetPort == 0 { - mapping.TargetPort = mapping.Port - } - return mapping, nil -} + ret, err := k8s.DeployFromFile.Create(s, params) + if err != nil { + return err + } + printObjectYAML(ret) + return nil + }) -func parsePortMappings(ports []string) (*jsonutils.JSONArray, error) { - ret := jsonutils.NewArray() - for _, port := range ports { - mapping, err := parsePortMapping(port) - if err != nil { - return nil, fmt.Errorf("Port %q error: %v", port, err) - } - ret.Add(jsonutils.Marshal(mapping)) - } - return ret, nil -} - -func parseNetConfig(net string) (*jsonutils.JSONDict, error) { - ret := jsonutils.NewDict() - for _, p := range strings.Split(net, ":") { - if regutils.MatchIP4Addr(p) { - ret.Add(jsonutils.NewString(p), "address") - } else { - ret.Add(jsonutils.NewString(p), "network") - } - } - return ret, nil + cmd.AddR(createCmd, createFromFileCmd) } diff --git a/cmd/climc/shell/k8s/ingress.go b/cmd/climc/shell/k8s/ingress.go index b93ab1464c..4ef4ce26a4 100644 --- a/cmd/climc/shell/k8s/ingress.go +++ b/cmd/climc/shell/k8s/ingress.go @@ -1,40 +1,9 @@ package k8s import ( - "yunion.io/x/jsonutils" - - "yunion.io/x/onecloud/pkg/mcclient" "yunion.io/x/onecloud/pkg/mcclient/modules/k8s" ) func initIngress() { - cmdN := func(suffix string) string { - return resourceCmdN("statefulset", suffix) - } - - R(&NamespaceResourceListOptions{}, cmdN("list"), "List k8s ingress", func(s *mcclient.ClientSession, args *NamespaceResourceListOptions) error { - ret, err := k8s.Ingresses.List(s, args.Params()) - if err != nil { - return err - } - PrintListResultTable(ret, k8s.Ingresses, s) - return nil - }) - - type getOpt struct { - resourceGetOptions - } - R(&getOpt{}, cmdN("show"), "Get ingress details", func(s *mcclient.ClientSession, args *getOpt) error { - id := args.NAME - params := args.ClusterParams() - if args.Namespace != "" { - params.Add(jsonutils.NewString(args.Namespace), "namespace") - } - ret, err := k8s.Ingresses.Get(s, id, params) - if err != nil { - return err - } - printObjectYAML(ret) - return nil - }) + initK8sNamespaceResource("ingress", k8s.Ingresses) } diff --git a/cmd/climc/shell/k8s/job.go b/cmd/climc/shell/k8s/job.go new file mode 100644 index 0000000000..f468ea0871 --- /dev/null +++ b/cmd/climc/shell/k8s/job.go @@ -0,0 +1,9 @@ +package k8s + +import ( + "yunion.io/x/onecloud/pkg/mcclient/modules/k8s" +) + +func initJob() { + initK8sNamespaceResource("job", k8s.Jobs) +} diff --git a/cmd/climc/shell/k8s/k8s.go b/cmd/climc/shell/k8s/k8s.go index dfa86745fc..963d72f668 100644 --- a/cmd/climc/shell/k8s/k8s.go +++ b/cmd/climc/shell/k8s/k8s.go @@ -5,8 +5,10 @@ import ( "yunion.io/x/jsonutils" "yunion.io/x/onecloud/cmd/climc/shell" + "yunion.io/x/onecloud/pkg/mcclient" "yunion.io/x/onecloud/pkg/mcclient/modules" "yunion.io/x/onecloud/pkg/mcclient/modules/k8s" + o "yunion.io/x/onecloud/pkg/mcclient/options/k8s" "yunion.io/x/onecloud/pkg/util/printutils" ) @@ -31,80 +33,12 @@ func init() { initIngress() initNamespace() initK8sNode() -} - -type clusterBaseOptions struct { - Cluster string `default:"$K8S_CLUSTER|default" help:"Kubernetes cluster name"` -} - -func (o clusterBaseOptions) ClusterParams() *jsonutils.JSONDict { - ret := jsonutils.NewDict() - ret.Add(jsonutils.NewString(o.Cluster), "cluster") - return ret -} - -type baseListOptions struct { - Limit int `default:"20" help:"Page limit"` - Offset int `default:"0" help:"page offset"` -} - -type NamespaceResourceListOptions struct { - namespaceListOptions - baseListOptions -} - -func (o NamespaceResourceListOptions) Params() *jsonutils.JSONDict { - params := fetchNamespaceParams(o.namespaceListOptions) - params.Update(fetchPagingParams(o.baseListOptions)) - params.Update(o.ClusterParams()) - return params -} - -func fetchPagingParams(opt baseListOptions) *jsonutils.JSONDict { - params := jsonutils.NewDict() - if opt.Limit > 0 { - params.Add(jsonutils.NewInt(int64(opt.Limit)), "limit") - } - if opt.Offset > 0 { - params.Add(jsonutils.NewInt(int64(opt.Offset)), "offset") - } - return params -} - -type namespaceListOptions struct { - namespaceOptions - AllNamespace bool `help:"Show resource in all namespace"` -} - -type namespaceOptions struct { - clusterBaseOptions - Namespace string `help:"Namespace of this resource"` -} - -type resourceGetOptions struct { - clusterBaseOptions - Namespace string `help:"Namespace of this resource"` - NAME string `help:"Name ident of the resource"` -} - -func (o resourceGetOptions) ToJSON() *jsonutils.JSONDict { - params := o.ClusterParams() - if o.Namespace != "" { - params.Add(jsonutils.NewString(o.Namespace), "namespace") - } - return params -} - -func fetchNamespaceParams(opt namespaceListOptions) *jsonutils.JSONDict { - params := jsonutils.NewDict() - if opt.AllNamespace { - params.Add(jsonutils.JSONTrue, "all_namespace") - return params - } - if opt.Namespace != "" { - params.Add(jsonutils.NewString(opt.Namespace), "namespace") - } - return params + initSecret() + initStorageClass() + initPV() + initPVC() + initJob() + initCronJob() } var ( @@ -132,3 +66,118 @@ func clusterContext(clusterId string) modules.ManagerContext { func printObjectYAML(obj jsonutils.JSONObject) { fmt.Println(obj.YAMLString()) } + +type Cmd struct { + Options interface{} + Command string + Desc string + Callback interface{} +} + +func NewCommand(options interface{}, command string, desc string, callback interface{}) *Cmd { + return &Cmd{ + Options: options, + Command: command, + Desc: desc, + Callback: callback, + } +} + +func (c Cmd) R() { + R(c.Options, c.Command, c.Desc, c.Callback) +} + +type ShellCommands struct { + Commands []*Cmd + CommandNameFactory func(suffix string) string +} + +func NewShellCommands(cmdN func(suffix string) string) *ShellCommands { + c := &ShellCommands{ + CommandNameFactory: cmdN, + } + c.Commands = make([]*Cmd, 0) + return c +} + +func (c *ShellCommands) AddR(rs ...*Cmd) *ShellCommands { + for _, r := range rs { + r.R() + c.Commands = append(c.Commands, r) + } + return c +} + +func initK8sClusterResource(kind string, manager modules.Manager) *ShellCommands { + cmdN := func(suffix string) string { + return resourceCmdN(kind, suffix) + } + + // List resource + listCmd := NewCommand( + &o.ResourceListOptions{}, + cmdN("list"), + fmt.Sprintf("List k8s %s", kind), + func(s *mcclient.ClientSession, args *o.ResourceListOptions) error { + ret, err := manager.List(s, args.Params()) + if err != nil { + return err + } + PrintListResultTable(ret, manager.(k8s.ListPrinter), s) + return nil + }, + ) + + // Get resource details + getCmd := NewCommand( + &o.ResourceGetOptions{}, + cmdN("show"), + fmt.Sprintf("Show k8s %s", kind), + func(s *mcclient.ClientSession, args *o.ResourceGetOptions) error { + ret, err := manager.Get(s, args.NAME, args.Params()) + if err != nil { + return err + } + printObjectYAML(ret) + return nil + }, + ) + return NewShellCommands(cmdN).AddR(listCmd, getCmd) +} + +func initK8sNamespaceResource(kind string, manager modules.Manager) *ShellCommands { + cmdN := func(suffix string) string { + return resourceCmdN(kind, suffix) + } + + // List resource + listCmd := NewCommand( + &o.NamespaceResourceListOptions{}, + cmdN("list"), + fmt.Sprintf("List k8s %s", kind), + func(s *mcclient.ClientSession, args *o.NamespaceResourceListOptions) error { + ret, err := manager.List(s, args.Params()) + if err != nil { + return err + } + PrintListResultTable(ret, manager.(k8s.ListPrinter), s) + return nil + }, + ) + + // Get resource details + getCmd := NewCommand( + &o.NamespaceResourceGetOptions{}, + cmdN("show"), + fmt.Sprintf("Show k8s %s", kind), + func(s *mcclient.ClientSession, args *o.NamespaceResourceGetOptions) error { + ret, err := manager.Get(s, args.NAME, args.Params()) + if err != nil { + return err + } + printObjectYAML(ret) + return nil + }, + ) + return NewShellCommands(cmdN).AddR(listCmd, getCmd) +} diff --git a/cmd/climc/shell/k8s/k8s_node.go b/cmd/climc/shell/k8s/k8s_node.go index 03750bd464..eb0206df03 100644 --- a/cmd/climc/shell/k8s/k8s_node.go +++ b/cmd/climc/shell/k8s/k8s_node.go @@ -1,41 +1,9 @@ package k8s import ( - "yunion.io/x/onecloud/pkg/mcclient" "yunion.io/x/onecloud/pkg/mcclient/modules/k8s" ) func initK8sNode() { - cmdN := func(suffix string) string { - return resourceCmdN("node", suffix) - } - - type listOpt struct { - clusterBaseOptions - baseListOptions - } - R(&listOpt{}, cmdN("list"), "List k8s nodes resource", func(s *mcclient.ClientSession, args *listOpt) error { - params := fetchPagingParams(args.baseListOptions) - params.Update(args.ClusterParams()) - ret, err := k8s.K8sNodes.List(s, params) - if err != nil { - return err - } - PrintListResultTable(ret, k8s.K8sNodes, s) - return nil - }) - - type getOpt struct { - clusterBaseOptions - NAME string `help:"Node name"` - } - R(&getOpt{}, cmdN("show"), "Show k8s node", func(s *mcclient.ClientSession, args *getOpt) error { - params := args.ClusterParams() - ret, err := k8s.K8sNodes.Get(s, args.NAME, params) - if err != nil { - return err - } - printObjectYAML(ret) - return nil - }) + initK8sClusterResource("node", k8s.K8sNodes) } diff --git a/cmd/climc/shell/k8s/namespace.go b/cmd/climc/shell/k8s/namespace.go index 65dcc77ab3..a83c154bf5 100644 --- a/cmd/climc/shell/k8s/namespace.go +++ b/cmd/climc/shell/k8s/namespace.go @@ -1,41 +1,9 @@ package k8s import ( - "yunion.io/x/onecloud/pkg/mcclient" "yunion.io/x/onecloud/pkg/mcclient/modules/k8s" ) func initNamespace() { - cmdN := func(suffix string) string { - return resourceCmdN("namespace", suffix) - } - - type listOpt struct { - clusterBaseOptions - baseListOptions - } - R(&listOpt{}, cmdN("list"), "List k8s namespace", func(s *mcclient.ClientSession, args *listOpt) error { - params := fetchPagingParams(args.baseListOptions) - params.Update(args.ClusterParams()) - ret, err := k8s.Namespaces.List(s, params) - if err != nil { - return err - } - PrintListResultTable(ret, k8s.Namespaces, s) - return nil - }) - - type getOpt struct { - clusterBaseOptions - NAME string `help:"Namespace name"` - } - R(&getOpt{}, cmdN("show"), "Show k8s namespace", func(s *mcclient.ClientSession, args *getOpt) error { - params := args.ClusterParams() - ret, err := k8s.Namespaces.Get(s, args.NAME, params) - if err != nil { - return err - } - printObjectYAML(ret) - return nil - }) + initK8sClusterResource("namespace", k8s.Namespaces) } diff --git a/cmd/climc/shell/k8s/nodes.go b/cmd/climc/shell/k8s/nodes.go index 7c6c9f2a0c..ac59cb05fa 100644 --- a/cmd/climc/shell/k8s/nodes.go +++ b/cmd/climc/shell/k8s/nodes.go @@ -3,31 +3,19 @@ package k8s import ( "fmt" - "yunion.io/x/jsonutils" - "yunion.io/x/onecloud/pkg/mcclient" "yunion.io/x/onecloud/pkg/mcclient/modules/k8s" - "yunion.io/x/onecloud/pkg/mcclient/options" + o "yunion.io/x/onecloud/pkg/mcclient/options/k8s" ) func initNode() { cmdN := func(suffix string) string { return kubeResourceCmdN("node", suffix) } - type listOpt struct { - options.BaseListOptions - Cluster string `help:"Filter by cluster"` - } - R(&listOpt{}, cmdN("list"), "List k8s infra nodes", func(s *mcclient.ClientSession, args *listOpt) error { - args.Details = options.Bool(true) - var params *jsonutils.JSONDict - { - var err error - params, err = options.ListStructToParams(args) - if err != nil { - return err - - } + R(&o.NodeListOptions{}, cmdN("list"), "List k8s infra nodes", func(s *mcclient.ClientSession, args *o.NodeListOptions) error { + params, err := args.Params() + if err != nil { + return err } result, err := k8s.Nodes.List(s, params) if err != nil { @@ -37,56 +25,8 @@ func initNode() { return nil }) - type createOpt struct { - CLUSTER string `help:"Cluster id"` - Etcd bool `help:"Etcd role"` - Controlplane bool `help:"Controlplane role"` - Worker bool `help:"Worker role"` - AllRole bool `help:"All roles"` - HostnameOverride string `help:"Worker node overrided hostname"` - Host string `help:"Yunion host server name or id"` - Name string `help:"Name of node"` - RegistryMirror []string `help:"Docker registry mirrors, e.g. 'https://registry.docker-cn.com'"` - InsecureRegistry []string `help:"Docker insecure registry"` - } - R(&createOpt{}, cmdN("create"), "Create k8s cluster node", func(s *mcclient.ClientSession, args *createOpt) error { - params := jsonutils.NewDict() - if args.Name != "" { - params.Add(jsonutils.NewString(args.Name), "name") - } - params.Add(jsonutils.NewString(args.CLUSTER), "cluster") - - dockerConf := dockerConfig{} - for _, rm := range args.RegistryMirror { - dockerConf.RegistryMirrors = append(dockerConf.RegistryMirrors, rm) - } - for _, im := range args.InsecureRegistry { - dockerConf.InsecureRegistries = append(dockerConf.InsecureRegistries, im) - } - confObj := jsonutils.Marshal(dockerConf) - params.Add(confObj, "dockerd_config") - - roles := jsonutils.NewArray() - if args.AllRole { - roles.Add(jsonutils.NewString("etcd"), jsonutils.NewString("controlplane"), jsonutils.NewString("worker")) - } else { - if args.Etcd { - roles.Add(jsonutils.NewString("etcd")) - } - if args.Controlplane { - roles.Add(jsonutils.NewString("controlplane")) - } - if args.Worker { - roles.Add(jsonutils.NewString("worker")) - } - } - params.Add(roles, "roles") - if args.HostnameOverride != "" { - params.Add(jsonutils.NewString(args.HostnameOverride), "hostname_override") - } - if args.Host != "" { - params.Add(jsonutils.NewString(args.Host), "host") - } + R(&o.NodeCreateOptions{}, cmdN("create"), "Create k8s cluster node", func(s *mcclient.ClientSession, args *o.NodeCreateOptions) error { + params := args.Params() node, err := k8s.Nodes.Create(s, params) if err != nil { return err @@ -95,23 +35,13 @@ func initNode() { return nil }) - type identOpt struct { - ID string `help:"ID or name of the node"` - } - - type identsOpt struct { - ID []string `help:"ID or name of the nodes"` - } - type deleteOpt struct { - identsOpt - } - R(&deleteOpt{}, cmdN("delete"), "Delete node", func(s *mcclient.ClientSession, args *deleteOpt) error { + R(&o.IdentsOptions{}, cmdN("delete"), "Delete node", func(s *mcclient.ClientSession, args *o.IdentsOptions) error { ret := k8s.Nodes.BatchDeleteWithParam(s, args.ID, nil, nil) printBatchResults(ret, k8s.Nodes.GetColumns(s)) return nil }) - R(&identOpt{}, cmdN("show"), "Show node details", func(s *mcclient.ClientSession, args *identOpt) error { + R(&o.IdentOptions{}, cmdN("show"), "Show node details", func(s *mcclient.ClientSession, args *o.IdentOptions) error { obj, err := k8s.Nodes.Get(s, args.ID, nil) if err != nil { return err @@ -120,13 +50,13 @@ func initNode() { return nil }) - R(&identsOpt{}, cmdN("purge"), "Purge a node record in database, not actually do deletion", func(s *mcclient.ClientSession, args *identsOpt) error { + R(&o.IdentsOptions{}, cmdN("purge"), "Purge a node record in database, not actually do deletion", func(s *mcclient.ClientSession, args *o.IdentsOptions) error { ret := k8s.Nodes.BatchPerformAction(s, args.ID, "purge", nil) printBatchResults(ret, k8s.Nodes.GetColumns(s)) return nil }) - R(&identOpt{}, cmdN("dockerconfig"), "Get docker daemon config", func(s *mcclient.ClientSession, args *identOpt) error { + R(&o.IdentOptions{}, cmdN("dockerconfig"), "Get docker daemon config", func(s *mcclient.ClientSession, args *o.IdentOptions) error { ret, err := k8s.Nodes.GetSpecific(s, args.ID, "docker-config", nil) if err != nil { return err diff --git a/cmd/climc/shell/k8s/pods.go b/cmd/climc/shell/k8s/pods.go index 72b7ba1d0a..8de0c7ed5d 100644 --- a/cmd/climc/shell/k8s/pods.go +++ b/cmd/climc/shell/k8s/pods.go @@ -1,55 +1,9 @@ package k8s import ( - "fmt" - - "yunion.io/x/jsonutils" - - "yunion.io/x/onecloud/pkg/mcclient" "yunion.io/x/onecloud/pkg/mcclient/modules/k8s" ) func initPod() { - cmdN := func(suffix string) string { - return resourceCmdN("pod", suffix) - } - - R(&NamespaceResourceListOptions{}, cmdN("list"), "List k8s pod", func(s *mcclient.ClientSession, args *NamespaceResourceListOptions) error { - ret, err := k8s.Pods.List(s, args.Params()) - if err != nil { - return err - } - PrintListResultTable(ret, k8s.Pods, s) - return nil - }) - - type getOpt struct { - resourceGetOptions - } - R(&getOpt{}, cmdN("show"), "Get pod details", func(s *mcclient.ClientSession, args *getOpt) error { - id := args.NAME - params := args.ClusterParams() - if args.Namespace != "" { - params.Add(jsonutils.NewString(args.Namespace), "namespace") - } - ret, err := k8s.Pods.Get(s, id, params) - if err != nil { - return err - } - printObjectYAML(ret) - return nil - }) - - type deleteOpt struct { - resourceGetOptions - } - R(&deleteOpt{}, cmdN("delete"), "Delete pod", func(s *mcclient.ClientSession, args *deleteOpt) error { - id := args.NAME - ret, err := k8s.Pods.Delete(s, id, args.ToJSON()) - if err != nil { - return err - } - fmt.Println(ret) - return nil - }) + initK8sNamespaceResource("pod", k8s.Pods) } diff --git a/cmd/climc/shell/k8s/pv.go b/cmd/climc/shell/k8s/pv.go new file mode 100644 index 0000000000..a1d4b14da6 --- /dev/null +++ b/cmd/climc/shell/k8s/pv.go @@ -0,0 +1,9 @@ +package k8s + +import ( + "yunion.io/x/onecloud/pkg/mcclient/modules/k8s" +) + +func initPV() { + initK8sClusterResource("persistentvolume", k8s.PersistentVolumes) +} diff --git a/cmd/climc/shell/k8s/pvc.go b/cmd/climc/shell/k8s/pvc.go new file mode 100644 index 0000000000..14a66d0693 --- /dev/null +++ b/cmd/climc/shell/k8s/pvc.go @@ -0,0 +1,9 @@ +package k8s + +import ( + "yunion.io/x/onecloud/pkg/mcclient/modules/k8s" +) + +func initPVC() { + initK8sNamespaceResource("persistentvolumeclaim", k8s.PersistentVolumeClaims) +} diff --git a/cmd/climc/shell/k8s/raw.go b/cmd/climc/shell/k8s/raw.go index cebcd968a8..e2643c9166 100644 --- a/cmd/climc/shell/k8s/raw.go +++ b/cmd/climc/shell/k8s/raw.go @@ -8,10 +8,11 @@ import ( "yunion.io/x/onecloud/pkg/mcclient" "yunion.io/x/onecloud/pkg/mcclient/modules/k8s" + o "yunion.io/x/onecloud/pkg/mcclient/options/k8s" ) type rawOpt struct { - namespaceOptions + o.NamespaceWithClusterOptions KIND string `help:"resource kind"` NAME string `help:"instance name"` } diff --git a/cmd/climc/shell/k8s/releases.go b/cmd/climc/shell/k8s/releases.go index 534b08e925..eaa4e41652 100644 --- a/cmd/climc/shell/k8s/releases.go +++ b/cmd/climc/shell/k8s/releases.go @@ -2,66 +2,18 @@ package k8s import ( "fmt" - "io/ioutil" - - json "yunion.io/x/jsonutils" "yunion.io/x/onecloud/pkg/mcclient" "yunion.io/x/onecloud/pkg/mcclient/modules/k8s" + o "yunion.io/x/onecloud/pkg/mcclient/options/k8s" ) func initRelease() { cmdN := func(suffix string) string { return resourceCmdN("release", suffix) } - type listOpt struct { - namespaceListOptions - baseListOptions - Name string `help:"Search by name"` - Filter string `help:"Filter, split by space"` - Admin bool `help:"Admin to show all namespace releases"` - Deployed bool `help:"Show deployed status releases"` - Deleted bool `help:"Show deleted status releases"` - Deleting bool `help:"Show deleting status releases"` - Failed bool `help:"Show failed status releases"` - Superseded bool `help:"Show superseded status releases"` - Pending bool `help:"Show pending status releases"` - } - R(&listOpt{}, cmdN("list"), "List k8s cluster helm releases", func(s *mcclient.ClientSession, args *listOpt) error { - params := fetchNamespaceParams(args.namespaceListOptions) - params.Update(fetchPagingParams(args.baseListOptions)) - params.Update(args.ClusterParams()) - if args.Filter != "" { - params.Add(json.NewString(args.Filter), "filter") - } - if args.Namespace != "" { - params.Add(json.NewString(args.Namespace), "namespace") - } - if args.Name != "" { - params.Add(json.NewString(args.Name), "name") - } - params.Add(json.JSONTrue, "all") - if args.Admin { - params.Add(json.JSONTrue, "admin") - } - if args.Deployed { - params.Add(json.JSONTrue, "deployed") - } - if args.Deleted { - params.Add(json.JSONTrue, "deleted") - } - if args.Deleting { - params.Add(json.JSONTrue, "deleting") - } - if args.Failed { - params.Add(json.JSONTrue, "failed") - } - if args.Superseded { - params.Add(json.JSONTrue, "superseded") - } - if args.Pending { - params.Add(json.JSONTrue, "pending") - } + R(&o.ReleaseListOptions{}, cmdN("list"), "List k8s cluster helm releases", func(s *mcclient.ClientSession, args *o.ReleaseListOptions) error { + params := args.Params() ret, err := k8s.Releases.List(s, params) if err != nil { return err @@ -70,12 +22,8 @@ func initRelease() { return nil }) - type showOpt struct { - clusterBaseOptions - NAME string `help:"Release instance name"` - } - R(&showOpt{}, cmdN("show"), "Get helm release details", func(s *mcclient.ClientSession, args *showOpt) error { - ret, err := k8s.Releases.Get(s, args.NAME, args.ClusterParams()) + R(&o.ResourceGetOptions{}, cmdN("show"), "Get helm release details", func(s *mcclient.ClientSession, args *o.ResourceGetOptions) error { + ret, err := k8s.Releases.Get(s, args.NAME, args.Params()) if err != nil { return err } @@ -87,53 +35,11 @@ func initRelease() { return nil }) - type releaseCUOpts struct { - Values string `help:"Specify values in a YAML file (can specify multiple)" short-token:"f"` - Version string `help:"Specify the exact chart version to install. If not specified, latest version installed"` - //Set []string `help:"set STRING values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)"` - DryRun bool `help:"Simulate an install"` - Details bool `help:"Show release deploy details, include kubernetes created resources"` - Timeout int64 `help:"Time in seconds to wait for any individual kubernetes operation (like Jobs for hooks)" default:"600"` - } - releaseCUDict := func(args releaseCUOpts) (*json.JSONDict, error) { - params := json.NewDict() - if args.Version != "" { - params.Add(json.NewString(args.Version), "version") - } - if args.DryRun { - params.Add(json.JSONTrue, "dry_run") - } - params.Add(json.NewInt(args.Timeout), "timeout") - if args.Values != "" { - //vals, err := helm.MergeValuesF(args.Values, args.Set, []string{}) - vals, err := ioutil.ReadFile(args.Values) - if err != nil { - return nil, err - } - params.Add(json.NewString(string(vals)), "values") - } - return params, nil - } - - type releaseCreateOpts struct { - namespaceOptions - releaseCUOpts - Name string `help:"Release name, If unspecified, it will autogenerate one for you"` - CHARTNAME string `help:"Helm chart name, e.g stable/etcd"` - } - R(&releaseCreateOpts{}, cmdN("create"), "Create release with specified helm chart", func(s *mcclient.ClientSession, args *releaseCreateOpts) error { - params, err := releaseCUDict(args.releaseCUOpts) + R(&o.ReleaseCreateOptions{}, cmdN("create"), "Create release with specified helm chart", func(s *mcclient.ClientSession, args *o.ReleaseCreateOptions) error { + params, err := args.Params() if err != nil { return err } - params.Update(args.ClusterParams()) - params.Add(json.NewString(args.CHARTNAME), "chart_name") - if args.Namespace != "" { - params.Add(json.NewString(args.Namespace), "namespace") - } - if args.Name != "" { - params.Add(json.NewString(args.Name), "release_name") - } ret, err := k8s.Releases.Create(s, params) if err != nil { return err @@ -142,28 +48,11 @@ func initRelease() { return nil }) - type releaseUpgradeOpts struct { - clusterBaseOptions - releaseCUOpts - NAME string `help:"Release instance name"` - CHARTNAME string `help:"Helm chart name, e.g stable/etcd"` - ReuseValues bool `help:"When upgrading, reuse the last release's values, and merge in any new values. If '--reset-values' is specified, this is ignored"` - ResetValues bool `help:"When upgrading, reset the values to the ones built into the chart"` - } - R(&releaseUpgradeOpts{}, cmdN("upgrade"), "Upgrade release", func(s *mcclient.ClientSession, args *releaseUpgradeOpts) error { - params, err := releaseCUDict(args.releaseCUOpts) + R(&o.ReleaseUpgradeOptions{}, cmdN("upgrade"), "Upgrade release", func(s *mcclient.ClientSession, args *o.ReleaseUpgradeOptions) error { + params, err := args.Params() if err != nil { return err } - params.Update(args.ClusterParams()) - params.Add(json.NewString(args.CHARTNAME), "chart_name") - params.Add(json.NewString(args.NAME), "release_name") - if args.ReuseValues { - params.Add(json.JSONTrue, "reuse_values") - } - if args.ResetValues { - params.Add(json.JSONTrue, "reset_values") - } res, err := k8s.Releases.Put(s, args.NAME, params) if err != nil { @@ -173,12 +62,8 @@ func initRelease() { return nil }) - type deleteOpt struct { - clusterBaseOptions - NAME string `help:"Release instance name"` - } - R(&deleteOpt{}, cmdN("delete"), "Delete release", func(s *mcclient.ClientSession, args *deleteOpt) error { - _, err := k8s.Releases.Delete(s, args.NAME, args.ClusterParams()) + R(&o.ReleaseDeleteOptions{}, cmdN("delete"), "Delete release", func(s *mcclient.ClientSession, args *o.ReleaseDeleteOptions) error { + _, err := k8s.Releases.Delete(s, args.NAME, args.Params()) return err }) } diff --git a/cmd/climc/shell/k8s/repos.go b/cmd/climc/shell/k8s/repos.go index a8633570c7..c423e69712 100644 --- a/cmd/climc/shell/k8s/repos.go +++ b/cmd/climc/shell/k8s/repos.go @@ -1,29 +1,19 @@ package k8s import ( - "yunion.io/x/jsonutils" - "yunion.io/x/onecloud/pkg/mcclient" "yunion.io/x/onecloud/pkg/mcclient/modules/k8s" - "yunion.io/x/onecloud/pkg/mcclient/options" + o "yunion.io/x/onecloud/pkg/mcclient/options/k8s" ) func initRepo() { cmdN := func(suffix string) string { return resourceCmdN("repo", suffix) } - type listOpt struct { - options.BaseListOptions - } - R(&listOpt{}, cmdN("list"), "List k8s global helm repos", func(s *mcclient.ClientSession, args *listOpt) error { - var params *jsonutils.JSONDict - { - var err error - params, err = args.BaseListOptions.Params() - if err != nil { - return err - - } + R(&o.RepoListOptions{}, cmdN("list"), "List k8s global helm repos", func(s *mcclient.ClientSession, args *o.RepoListOptions) error { + params, err := args.Params() + if err != nil { + return err } result, err := k8s.Repos.List(s, params) if err != nil { @@ -33,10 +23,7 @@ func initRepo() { return nil }) - type getOpt struct { - NAME string `help:"ID or name of the repo"` - } - R(&getOpt{}, cmdN("show"), "Show details of a repo", func(s *mcclient.ClientSession, args *getOpt) error { + R(&o.RepoGetOptions{}, cmdN("show"), "Show details of a repo", func(s *mcclient.ClientSession, args *o.RepoGetOptions) error { repo, err := k8s.Repos.Get(s, args.NAME, nil) if err != nil { return err @@ -45,19 +32,8 @@ func initRepo() { return nil }) - type createOpt struct { - getOpt - URL string `help:"Repository url"` - Public bool `help:"Make repository public"` - } - R(&createOpt{}, cmdN("create"), "Add repository", func(s *mcclient.ClientSession, args *createOpt) error { - params := jsonutils.NewDict() - params.Add(jsonutils.NewString(args.NAME), "name") - params.Add(jsonutils.NewString(args.URL), "url") - if args.Public { - params.Add(jsonutils.JSONTrue, "is_public") - } - repo, err := k8s.Repos.Create(s, params) + R(&o.RepoCreateOptions{}, cmdN("create"), "Add repository", func(s *mcclient.ClientSession, args *o.RepoCreateOptions) error { + repo, err := k8s.Repos.Create(s, args.Params()) if err != nil { return err } @@ -65,20 +41,8 @@ func initRepo() { return nil }) - type updateOpt struct { - getOpt - Name string `help:"Repository name to change"` - Url string `help:"Repository url to change"` - } - R(&updateOpt{}, cmdN("update"), "Update helm repository", func(s *mcclient.ClientSession, args *updateOpt) error { - params := jsonutils.NewDict() - if args.Name != "" { - params.Add(jsonutils.NewString(args.Name), "name") - } - if args.Url != "" { - params.Add(jsonutils.NewString(args.Url), "url") - } - repo, err := k8s.Repos.Update(s, args.NAME, params) + R(&o.RepoUpdateOptions{}, cmdN("update"), "Update helm repository", func(s *mcclient.ClientSession, args *o.RepoUpdateOptions) error { + repo, err := k8s.Repos.Update(s, args.NAME, args.Params()) if err != nil { return err } @@ -86,25 +50,7 @@ func initRepo() { return nil }) - R(&getOpt{}, cmdN("private"), "Make repository private", func(s *mcclient.ClientSession, args *getOpt) error { - repo, err := k8s.Repos.PerformAction(s, args.NAME, "private", nil) - if err != nil { - return err - } - printObject(repo) - return nil - }) - - R(&getOpt{}, cmdN("public"), "Make repository public", func(s *mcclient.ClientSession, args *getOpt) error { - repo, err := k8s.Repos.PerformAction(s, args.NAME, "public", nil) - if err != nil { - return err - } - printObject(repo) - return nil - }) - - R(&getOpt{}, cmdN("delete"), "Delete a repository", func(s *mcclient.ClientSession, args *getOpt) error { + R(&o.RepoGetOptions{}, cmdN("delete"), "Delete a repository", func(s *mcclient.ClientSession, args *o.RepoGetOptions) error { repo, err := k8s.Repos.Delete(s, args.NAME, nil) if err != nil { return err diff --git a/cmd/climc/shell/k8s/secret.go b/cmd/climc/shell/k8s/secret.go new file mode 100644 index 0000000000..f8ae6cc412 --- /dev/null +++ b/cmd/climc/shell/k8s/secret.go @@ -0,0 +1,9 @@ +package k8s + +import ( + "yunion.io/x/onecloud/pkg/mcclient/modules/k8s" +) + +func initSecret() { + initK8sNamespaceResource("secret", k8s.Secrets) +} diff --git a/cmd/climc/shell/k8s/service.go b/cmd/climc/shell/k8s/service.go index 8803d17607..6fc63eb83b 100644 --- a/cmd/climc/shell/k8s/service.go +++ b/cmd/climc/shell/k8s/service.go @@ -1,28 +1,9 @@ package k8s import ( - "yunion.io/x/onecloud/pkg/mcclient" "yunion.io/x/onecloud/pkg/mcclient/modules/k8s" ) func initService() { - cmdN := func(suffix string) string { - return resourceCmdN("service", suffix) - } - - type listOpt struct { - namespaceListOptions - baseListOptions - } - R(&listOpt{}, cmdN("list"), "List k8s service", func(s *mcclient.ClientSession, args *listOpt) error { - params := fetchNamespaceParams(args.namespaceListOptions) - params.Update(fetchPagingParams(args.baseListOptions)) - params.Update(args.ClusterParams()) - ret, err := k8s.Services.List(s, params) - if err != nil { - return err - } - PrintListResultTable(ret, k8s.Services, s) - return nil - }) + initK8sNamespaceResource("service", k8s.Services) } diff --git a/cmd/climc/shell/k8s/statefulset.go b/cmd/climc/shell/k8s/statefulset.go index 3c20e5d6a6..371fe03052 100644 --- a/cmd/climc/shell/k8s/statefulset.go +++ b/cmd/climc/shell/k8s/statefulset.go @@ -1,40 +1,9 @@ package k8s import ( - "yunion.io/x/jsonutils" - - "yunion.io/x/onecloud/pkg/mcclient" "yunion.io/x/onecloud/pkg/mcclient/modules/k8s" ) func initStatefulset() { - cmdN := func(suffix string) string { - return resourceCmdN("statefulset", suffix) - } - - R(&NamespaceResourceListOptions{}, cmdN("list"), "List k8s statefulset", func(s *mcclient.ClientSession, args *NamespaceResourceListOptions) error { - ret, err := k8s.StatefulSets.List(s, args.Params()) - if err != nil { - return err - } - PrintListResultTable(ret, k8s.StatefulSets, s) - return nil - }) - - type getOpt struct { - resourceGetOptions - } - R(&getOpt{}, cmdN("show"), "Get statefulset details", func(s *mcclient.ClientSession, args *getOpt) error { - id := args.NAME - params := args.ClusterParams() - if args.Namespace != "" { - params.Add(jsonutils.NewString(args.Namespace), "namespace") - } - ret, err := k8s.StatefulSets.Get(s, id, params) - if err != nil { - return err - } - printObjectYAML(ret) - return nil - }) + initK8sNamespaceResource("statefulset", k8s.StatefulSets) } diff --git a/cmd/climc/shell/k8s/storageclass.go b/cmd/climc/shell/k8s/storageclass.go new file mode 100644 index 0000000000..507e5a36aa --- /dev/null +++ b/cmd/climc/shell/k8s/storageclass.go @@ -0,0 +1,9 @@ +package k8s + +import ( + "yunion.io/x/onecloud/pkg/mcclient/modules/k8s" +) + +func initStorageClass() { + initK8sClusterResource("storageclass", k8s.Storageclass) +} diff --git a/cmd/climc/shell/k8s/tiller.go b/cmd/climc/shell/k8s/tiller.go index 610dcb0e5e..38f4dfc7f9 100644 --- a/cmd/climc/shell/k8s/tiller.go +++ b/cmd/climc/shell/k8s/tiller.go @@ -1,51 +1,17 @@ package k8s import ( - json "yunion.io/x/jsonutils" - "yunion.io/x/onecloud/pkg/mcclient" "yunion.io/x/onecloud/pkg/mcclient/modules/k8s" + o "yunion.io/x/onecloud/pkg/mcclient/options/k8s" ) func initTiller() { cmdN := func(suffix string) string { return resourceCmdN("tiller", suffix) } - type createOpt struct { - clusterBaseOptions - KubeContext string `json:"kube_context"` - Namespace string `json:"namespace" default:"kube-system"` - // Upgrade if Tiller is already installed - Upgrade bool `json:"upgrade"` - // Name of service account - ServiceAccount string `json:"service_account" default:"tiller"` - // Use the canary Tiller image - Canary bool `json:"canary_image"` - - // Override Tiller image - Image string `json:"tiller_image" default:"yunion/tiller:v2.9.0"` - // Limit the maximum number of revisions saved per release. Use 0 for no limit. - MaxHistory int `json:"history_max"` - } - R(&createOpt{}, cmdN("create"), "Install helm tiller server to Kubernetes cluster", func(s *mcclient.ClientSession, args *createOpt) error { - params := args.ClusterParams() - if len(args.KubeContext) > 0 { - params.Add(json.NewString(args.KubeContext), "kube_context") - } - params.Add(json.NewString(args.Namespace), "namespace") - params.Add(json.NewString(args.ServiceAccount), "service_account") - if args.Canary { - params.Add(json.JSONTrue, "canary_image") - } - if args.Upgrade { - params.Add(json.JSONTrue, "upgrade") - } - if len(args.Image) > 0 { - params.Add(json.NewString(args.Image), "tiller_image") - } - if args.MaxHistory > 0 { - params.Add(json.NewInt(int64(args.MaxHistory)), "history_max") - } + R(&o.TillerCreateOptions{}, cmdN("create"), "Install helm tiller server to Kubernetes cluster", func(s *mcclient.ClientSession, args *o.TillerCreateOptions) error { + params := args.Params() ret, err := k8s.Tiller.Create(s, params) if err != nil { return err diff --git a/pkg/mcclient/modules/k8s/cronjob.go b/pkg/mcclient/modules/k8s/cronjob.go new file mode 100644 index 0000000000..c890948982 --- /dev/null +++ b/pkg/mcclient/modules/k8s/cronjob.go @@ -0,0 +1,19 @@ +package k8s + +import ( + "yunion.io/x/onecloud/pkg/mcclient/modules" +) + +var CronJobs *CronJobManager + +type CronJobManager struct { + *NamespaceResourceManager +} + +func init() { + CronJobs = &CronJobManager{ + NamespaceResourceManager: NewNamespaceResourceManager("cronjob", "cronjobs", NewNamespaceCols(), NewColumns()), + } + + modules.Register(CronJobs) +} diff --git a/pkg/mcclient/modules/k8s/job.go b/pkg/mcclient/modules/k8s/job.go new file mode 100644 index 0000000000..8b0fcef3cb --- /dev/null +++ b/pkg/mcclient/modules/k8s/job.go @@ -0,0 +1,19 @@ +package k8s + +import ( + "yunion.io/x/onecloud/pkg/mcclient/modules" +) + +var Jobs *JobManager + +type JobManager struct { + *NamespaceResourceManager +} + +func init() { + Jobs = &JobManager{ + NamespaceResourceManager: NewNamespaceResourceManager("job", "jobs", NewNamespaceCols(), NewColumns()), + } + + modules.Register(Jobs) +} diff --git a/pkg/mcclient/modules/k8s/pv.go b/pkg/mcclient/modules/k8s/pv.go new file mode 100644 index 0000000000..152e880827 --- /dev/null +++ b/pkg/mcclient/modules/k8s/pv.go @@ -0,0 +1,38 @@ +package k8s + +import ( + "yunion.io/x/jsonutils" + + "yunion.io/x/onecloud/pkg/mcclient/modules" +) + +var PersistentVolumes *PersistentVolumeManager + +type PersistentVolumeManager struct { + *MetaResourceManager +} + +func init() { + PersistentVolumes = &PersistentVolumeManager{ + MetaResourceManager: NewMetaResourceManager("persistentvolume", "persistentvolumes", + NewColumns("StorageClass", "Claim", "AccessModes"), + NewColumns()), + } + + modules.Register(PersistentVolumes) +} + +func (m PersistentVolumeManager) GetStorageClass(obj jsonutils.JSONObject) interface{} { + sc, _ := obj.GetString("storageClass") + return sc +} + +func (m PersistentVolumeManager) GetClaim(obj jsonutils.JSONObject) interface{} { + claim, _ := obj.GetString("claim") + return claim +} + +func (m PersistentVolumeManager) GetAccessModes(obj jsonutils.JSONObject) interface{} { + modes, _ := obj.(*jsonutils.JSONDict).GetArray("accessModes") + return modes +} diff --git a/pkg/mcclient/modules/k8s/pvc.go b/pkg/mcclient/modules/k8s/pvc.go new file mode 100644 index 0000000000..f248e26514 --- /dev/null +++ b/pkg/mcclient/modules/k8s/pvc.go @@ -0,0 +1,34 @@ +package k8s + +import ( + "yunion.io/x/jsonutils" + + "yunion.io/x/onecloud/pkg/mcclient/modules" +) + +var PersistentVolumeClaims *PersistentVolumeClaimManager + +type PersistentVolumeClaimManager struct { + *NamespaceResourceManager + statusGetter +} + +func init() { + PersistentVolumeClaims = &PersistentVolumeClaimManager{ + NamespaceResourceManager: NewNamespaceResourceManager( + "persistentvolumeclaim", "persistentvolumeclaims", + NewColumns("Status", "Volume", "StorageClass"), NewColumns()), + statusGetter: getStatus, + } + modules.Register(PersistentVolumeClaims) +} + +func (m PersistentVolumeClaimManager) GetVolume(obj jsonutils.JSONObject) interface{} { + volume, _ := obj.GetString("volume") + return volume +} + +func (m PersistentVolumeClaimManager) GetStorageClass(obj jsonutils.JSONObject) interface{} { + sc, _ := obj.GetString("storageClass") + return sc +} diff --git a/pkg/mcclient/modules/k8s/secret.go b/pkg/mcclient/modules/k8s/secret.go new file mode 100644 index 0000000000..b66a27a9e5 --- /dev/null +++ b/pkg/mcclient/modules/k8s/secret.go @@ -0,0 +1,25 @@ +package k8s + +import ( + "yunion.io/x/jsonutils" + + "yunion.io/x/onecloud/pkg/mcclient/modules" +) + +var Secrets *SecretManager + +type SecretManager struct { + *NamespaceResourceManager +} + +func init() { + Secrets = &SecretManager{ + NewNamespaceResourceManager("secret", "secrets", + NewNamespaceCols("Type"), NewColumns())} + modules.Register(Secrets) +} + +func (m SecretManager) GetType(obj jsonutils.JSONObject) interface{} { + typ, _ := obj.GetString("type") + return typ +} diff --git a/pkg/mcclient/modules/k8s/storageclass.go b/pkg/mcclient/modules/k8s/storageclass.go new file mode 100644 index 0000000000..1db927ff6f --- /dev/null +++ b/pkg/mcclient/modules/k8s/storageclass.go @@ -0,0 +1,26 @@ +package k8s + +import ( + "yunion.io/x/jsonutils" + + "yunion.io/x/onecloud/pkg/mcclient/modules" +) + +var Storageclass *StorageclassManager + +type StorageclassManager struct { + *MetaResourceManager +} + +func init() { + Storageclass = &StorageclassManager{ + MetaResourceManager: NewMetaResourceManager("storageclass", "storageclasses", NewColumns("Provisioner"), NewColumns()), + } + + modules.Register(Storageclass) +} + +func (m StorageclassManager) GetProvisioner(obj jsonutils.JSONObject) interface{} { + provisioner, _ := obj.GetString("provisioner") + return provisioner +} diff --git a/pkg/mcclient/options/k8s/base.go b/pkg/mcclient/options/k8s/base.go new file mode 100644 index 0000000000..384c027251 --- /dev/null +++ b/pkg/mcclient/options/k8s/base.go @@ -0,0 +1,104 @@ +package k8s + +import ( + "yunion.io/x/jsonutils" +) + +type ClusterBaseOptions struct { + Cluster string `default:"$K8S_CLUSTER|default" help:"Kubernetes cluster name"` +} + +func (o ClusterBaseOptions) Params() *jsonutils.JSONDict { + ret := jsonutils.NewDict() + ret.Add(jsonutils.NewString(o.Cluster), "cluster") + return ret +} + +type BaseListOptions struct { + Limit int `default:"20" help:"Page limit"` + Offset int `default:"0" help:"page offset"` +} + +func (o BaseListOptions) Params() *jsonutils.JSONDict { + params := jsonutils.NewDict() + if o.Limit > 0 { + params.Add(jsonutils.NewInt(int64(o.Limit)), "limit") + } + if o.Offset > 0 { + params.Add(jsonutils.NewInt(int64(o.Offset)), "offset") + } + return params +} + +type ResourceListOptions struct { + ClusterBaseOptions + BaseListOptions +} + +func (o ResourceListOptions) Params() *jsonutils.JSONDict { + params := o.BaseListOptions.Params() + params.Update(o.ClusterBaseOptions.Params()) + return params +} + +type ResourceGetOptions struct { + ClusterBaseOptions + NAME string `help:"Name ident of the resource"` +} + +func (o ResourceGetOptions) Params() *jsonutils.JSONDict { + params := o.ClusterBaseOptions.Params() + return params +} + +type NamespaceResourceListOptions struct { + ResourceListOptions + Namespace string `help:"Namespace of this resource"` + AllNamespace bool `help:"Show resource in all namespace"` +} + +func (o NamespaceResourceListOptions) Params() *jsonutils.JSONDict { + params := o.ResourceListOptions.Params() + if o.AllNamespace { + params.Add(jsonutils.JSONTrue, "all_namespace") + return params + } + if o.Namespace != "" { + params.Add(jsonutils.NewString(o.Namespace), "namespace") + } + return params +} + +type NamespaceOptions struct { + Namespace string `help:"Namespace of this resource"` +} + +func (o NamespaceOptions) Params() *jsonutils.JSONDict { + params := jsonutils.NewDict() + if o.Namespace != "" { + params.Add(jsonutils.NewString(o.Namespace), "namespace") + } + return params +} + +type NamespaceResourceGetOptions struct { + ResourceGetOptions + NamespaceOptions +} + +func (o NamespaceResourceGetOptions) Params() *jsonutils.JSONDict { + params := o.ResourceGetOptions.Params() + params.Update(o.NamespaceOptions.Params()) + return params +} + +type NamespaceWithClusterOptions struct { + NamespaceOptions + ClusterBaseOptions +} + +func (o NamespaceWithClusterOptions) Params() *jsonutils.JSONDict { + params := o.ClusterBaseOptions.Params() + params.Update(o.NamespaceOptions.Params()) + return params +} diff --git a/pkg/mcclient/options/k8s/charts.go b/pkg/mcclient/options/k8s/charts.go new file mode 100644 index 0000000000..427e63622d --- /dev/null +++ b/pkg/mcclient/options/k8s/charts.go @@ -0,0 +1,49 @@ +package k8s + +import ( + "yunion.io/x/jsonutils" +) + +type ChartListOptions struct { + BaseListOptions + Name string `help:"Chart name"` + Repo string `help:"Repository name"` + RepoUrl string `help:"Repository url"` + AllVersion bool `json:"Get Chart all history versions"` + Keyword string `json:"Chart keyword"` +} + +func (o ChartListOptions) Params() *jsonutils.JSONDict { + params := o.BaseListOptions.Params() + if len(o.Name) != 0 { + params.Add(jsonutils.NewString(o.Name), "name") + } + if len(o.Repo) != 0 { + params.Add(jsonutils.NewString(o.Repo), "repo") + } + if len(o.RepoUrl) != 0 { + params.Add(jsonutils.NewString(o.RepoUrl), "repo_url") + } + if o.AllVersion { + params.Add(jsonutils.JSONTrue, "all_version") + } + if len(o.Keyword) != 0 { + params.Add(jsonutils.NewString(o.Keyword), "keyword") + } + return params +} + +type ChartGetOptions struct { + REPO string `help:"Repo of the chart"` + NAME string `help:"Chart name"` + Version string `help:"Chart version"` +} + +func (o ChartGetOptions) Params() *jsonutils.JSONDict { + params := jsonutils.NewDict() + params.Add(jsonutils.NewString(o.REPO), "repo") + if o.Version != "" { + params.Add(jsonutils.NewString(o.Version), "version") + } + return params +} diff --git a/pkg/mcclient/options/k8s/cluster.go b/pkg/mcclient/options/k8s/cluster.go new file mode 100644 index 0000000000..99d8722819 --- /dev/null +++ b/pkg/mcclient/options/k8s/cluster.go @@ -0,0 +1,177 @@ +package k8s + +import ( + "fmt" + "io/ioutil" + "strings" + + "yunion.io/x/jsonutils" + "yunion.io/x/pkg/util/sets" + + "yunion.io/x/onecloud/pkg/mcclient/options" +) + +type ClusterListOptions struct { + options.BaseListOptions +} + +func (o ClusterListOptions) Params() *jsonutils.JSONDict { + o.Details = options.Bool(true) + params, err := o.BaseListOptions.Params() + if err != nil { + panic(err) + } + return params +} + +type ClusterCreateOptions struct { + NAME string `help:"Name of cluster"` + Mode string `help:"Cluster mode" choices:"internal"` + K8sVersion string `help:"Cluster kubernetes components version" choices:"v1.8.10|v1.9.5|v1.10.0"` + InfraImage string `help:"Cluster kubelet infra container image"` + Cidr string `help:"Cluster service CIDR, e.g. 10.43.0.0/16"` + Domain string `help:"Cluster pod domain, e.g. cluster.local"` +} + +func (o ClusterCreateOptions) Params() *jsonutils.JSONDict { + params := jsonutils.NewDict() + params.Add(jsonutils.NewString(o.NAME), "name") + if o.Mode != "" { + params.Add(jsonutils.NewString(o.Mode), "mode") + } + if o.K8sVersion != "" { + params.Add(jsonutils.NewString(o.K8sVersion), "k8s_version") + } + if o.InfraImage != "" { + params.Add(jsonutils.NewString(o.InfraImage), "infra_container_image") + } + if o.Cidr != "" { + params.Add(jsonutils.NewString(o.Cidr), "cluster_cidr") + } + if o.Domain != "" { + params.Add(jsonutils.NewString(o.Domain), "cluster_domain") + } + return params +} + +type ClusterImportOptions struct { + NAME string `help:"Name of cluster to import"` + KUBECONFIG string `help:"Kubernetes auth config"` +} + +func (o ClusterImportOptions) Params() (*jsonutils.JSONDict, error) { + kubeconfig, err := ioutil.ReadFile(o.KUBECONFIG) + if err != nil { + return nil, fmt.Errorf("Read kube config %q error: %v", o.KUBECONFIG, err) + } + params := jsonutils.NewDict() + params.Add(jsonutils.NewString(string(kubeconfig)), "kube_config") + return params, nil +} + +type ClusterUpdateOptions struct { + NAME string `help:"Name of cluster"` + K8sVersion string `help:"Cluster kubernetes components version" choices:"v1.8.10|v1.9.5|v1.10.0"` +} + +func (o ClusterUpdateOptions) Params() *jsonutils.JSONDict { + params := jsonutils.NewDict() + if o.K8sVersion != "" { + params.Add(jsonutils.NewString(o.K8sVersion), "k8s_version") + } + return params +} + +type IdentOptions struct { + ID string `help:"ID or name of the model"` +} + +type IdentsOptions struct { + ID []string `help:"ID of models to operate"` +} + +type ClusterDeployOptions struct { + IdentOptions + Force bool `help:"Force deploy"` +} + +func (o ClusterDeployOptions) Params() *jsonutils.JSONDict { + params := jsonutils.NewDict() + if o.Force { + params.Add(jsonutils.JSONTrue, "force") + } + return params +} + +type ClusterDeleteOptions struct { + IdentsOptions +} + +type ClusterKubeconfigOptions struct { + IdentOptions + Directly bool `help:"Get directly connect kubeconfig"` +} + +func (o ClusterKubeconfigOptions) Params() *jsonutils.JSONDict { + params := jsonutils.NewDict() + if o.Directly { + params.Add(jsonutils.JSONTrue, "directly") + } + return params +} + +type ClusterAddNodesOptions struct { + IdentOptions + NodeConfig []string `help:"Node spec, 'host:[roles]' e.g: --node-config host01:controlplane,etcd,worker --node-config host02:worker"` + AutoDeploy bool `help:"Auto deploy"` +} + +func (o ClusterAddNodesOptions) Params() (*jsonutils.JSONDict, error) { + params := jsonutils.NewDict() + if o.AutoDeploy { + params.Add(jsonutils.JSONTrue, "auto_deploy") + } + nodesArray := jsonutils.NewArray() + for _, config := range o.NodeConfig { + opt, err := parseNodeAddConfigStr(config) + if err != nil { + return nil, err + } + nodesArray.Add(jsonutils.Marshal(opt)) + } + params.Add(nodesArray, "nodes") + return params, nil +} + +type dockerConfig struct { + RegistryMirrors []string `json:"registry-mirrors"` + InsecureRegistries []string `json:"insecure-registries"` +} + +type nodeAddConfig struct { + Host string `json:"host"` + Roles []string `json:"roles"` + Name string `json:"name"` + HostnameOverride string `json:"hostname_override"` + DockerdConfig dockerConfig `json:"dockerd_config"` +} + +func parseNodeAddConfigStr(config string) (nodeAddConfig, error) { + ret := nodeAddConfig{} + parts := strings.Split(config, ":") + if len(parts) != 2 { + return ret, fmt.Errorf("Invalid config: %q", config) + } + host := parts[0] + roleStr := parts[1] + ret.Host = host + roles := []string{} + for _, role := range strings.Split(roleStr, ",") { + if !sets.NewString("etcd", "controlplane", "worker").Has(role) { + return ret, fmt.Errorf("Invalid role: %q", role) + } + roles = append(roles, role) + } + ret.Roles = roles + return ret, nil +} diff --git a/pkg/mcclient/options/k8s/deployment.go b/pkg/mcclient/options/k8s/deployment.go new file mode 100644 index 0000000000..7d69632601 --- /dev/null +++ b/pkg/mcclient/options/k8s/deployment.go @@ -0,0 +1,147 @@ +package k8s + +import ( + "fmt" + "io/ioutil" + "strconv" + "strings" + + "yunion.io/x/jsonutils" + "yunion.io/x/pkg/util/regutils" + "yunion.io/x/pkg/util/sets" +) + +type DeploymentCreateOptions struct { + NamespaceWithClusterOptions + NAME string `help:"Name of deployment"` + Image string `help:"The image for the container to run"` + Replicas int64 `help:"Number of replicas for pods in this deployment"` + RunAsPrivileged bool `help:"Whether to run the container as privileged user"` + Labels string `help:"Comma separated labels to apply to the pod(s)"` + Env []string `help:"Environment variables to set in container"` + Port []string `help:"Port for the service that is created, format is :: e.g. tcp:80:3000"` + Net string `help:"Network config, e.g. net1, net1:10.168.222.171"` +} + +func (o DeploymentCreateOptions) Params() (*jsonutils.JSONDict, error) { + params := o.NamespaceWithClusterOptions.Params() + params.Add(jsonutils.NewString(o.NAME), "name") + if len(o.Image) == 0 { + return nil, fmt.Errorf("Image must provided") + } + params.Add(jsonutils.NewString(o.Image), "containerImage") + if o.Replicas > 1 { + params.Add(jsonutils.NewInt(o.Replicas), "replicas") + } + if o.RunAsPrivileged { + params.Add(jsonutils.JSONTrue, "runAsPrivileged") + } + if len(o.Port) != 0 { + portMappings, err := parsePortMappings(o.Port) + if err != nil { + return nil, err + } + params.Add(portMappings, "portMappings") + } + envList := jsonutils.NewArray() + for _, env := range o.Env { + parts := strings.Split(env, "=") + if len(parts) != 2 { + return nil, fmt.Errorf("Bad env value: %v", env) + } + envObj := jsonutils.NewDict() + envObj.Add(jsonutils.NewString(parts[0]), "name") + envObj.Add(jsonutils.NewString(parts[1]), "value") + envList.Add(envObj) + } + params.Add(envList, "variables") + if o.Net != "" { + net, err := parseNetConfig(o.Net) + if err != nil { + return nil, err + } + params.Add(net, "networkConfig") + } + return params, nil +} + +type portMapping struct { + Port int32 `json:"port"` + TargetPort int32 `json:"targetPort"` + Protocol string `json:"protocol"` +} + +func parsePortMapping(port string) (*portMapping, error) { + if len(port) == 0 { + return nil, fmt.Errorf("empty port mapping desc string") + } + parts := strings.Split(port, ":") + mapping := &portMapping{} + for _, part := range parts { + if sets.NewString("tcp", "udp").Has(strings.ToLower(part)) { + mapping.Protocol = strings.ToUpper(part) + } + if port, err := strconv.Atoi(part); err != nil { + continue + } else { + if mapping.Port == 0 { + mapping.Port = int32(port) + } else { + mapping.TargetPort = int32(port) + } + } + } + if mapping.Protocol == "" { + mapping.Protocol = "TCP" + } + if mapping.Port <= 0 { + return nil, fmt.Errorf("Service port not provided") + } + if mapping.TargetPort < 0 { + return nil, fmt.Errorf("Container invalid targetPort %d", mapping.TargetPort) + } + if mapping.TargetPort == 0 { + mapping.TargetPort = mapping.Port + } + return mapping, nil +} + +func parsePortMappings(ports []string) (*jsonutils.JSONArray, error) { + ret := jsonutils.NewArray() + for _, port := range ports { + mapping, err := parsePortMapping(port) + if err != nil { + return nil, fmt.Errorf("Port %q error: %v", port, err) + } + ret.Add(jsonutils.Marshal(mapping)) + } + return ret, nil +} + +func parseNetConfig(net string) (*jsonutils.JSONDict, error) { + ret := jsonutils.NewDict() + for _, p := range strings.Split(net, ":") { + if regutils.MatchIP4Addr(p) { + ret.Add(jsonutils.NewString(p), "address") + } else { + ret.Add(jsonutils.NewString(p), "network") + } + } + return ret, nil +} + +type DeploymentCreateFromFileOptions struct { + NamespaceResourceGetOptions + FILE string `help:"K8s resource YAML or JSON file"` +} + +func (o DeploymentCreateFromFileOptions) Params() (*jsonutils.JSONDict, error) { + params := o.NamespaceResourceGetOptions.Params() + params.Add(jsonutils.NewString(o.NAME), "name") + content, err := ioutil.ReadFile(o.FILE) + if err != nil { + return nil, err + } + params.Add(jsonutils.NewString(string(content)), "content") + return params, nil +} diff --git a/pkg/mcclient/options/k8s/node.go b/pkg/mcclient/options/k8s/node.go new file mode 100644 index 0000000000..2a516b1635 --- /dev/null +++ b/pkg/mcclient/options/k8s/node.go @@ -0,0 +1,69 @@ +package k8s + +import ( + "yunion.io/x/jsonutils" + + "yunion.io/x/onecloud/pkg/mcclient/options" +) + +type NodeListOptions struct { + options.BaseListOptions + Cluster string `help:"Filter by cluster"` +} + +func (o NodeListOptions) Params() (*jsonutils.JSONDict, error) { + return options.ListStructToParams(&o) +} + +type NodeCreateOptions struct { + CLUSTER string `help:"Cluster id"` + Etcd bool `help:"Etcd role"` + Controlplane bool `help:"Controlplane role"` + Worker bool `help:"Worker role"` + AllRole bool `help:"All roles"` + HostnameOverride string `help:"Worker node overrided hostname"` + Host string `help:"Yunion host server name or id"` + Name string `help:"Name of node"` + RegistryMirror []string `help:"Docker registry mirrors, e.g. 'https://registry.docker-cn.com'"` + InsecureRegistry []string `help:"Docker insecure registry"` +} + +func (o NodeCreateOptions) Params() *jsonutils.JSONDict { + params := jsonutils.NewDict() + if o.Name != "" { + params.Add(jsonutils.NewString(o.Name), "name") + } + params.Add(jsonutils.NewString(o.CLUSTER), "cluster") + dockerConf := dockerConfig{} + for _, rm := range o.RegistryMirror { + dockerConf.RegistryMirrors = append(dockerConf.RegistryMirrors, rm) + } + for _, im := range o.InsecureRegistry { + dockerConf.InsecureRegistries = append(dockerConf.InsecureRegistries, im) + } + confObj := jsonutils.Marshal(dockerConf) + params.Add(confObj, "dockerd_config") + + roles := jsonutils.NewArray() + if o.AllRole { + roles.Add(jsonutils.NewString("etcd"), jsonutils.NewString("controlplane"), jsonutils.NewString("worker")) + } else { + if o.Etcd { + roles.Add(jsonutils.NewString("etcd")) + } + if o.Controlplane { + roles.Add(jsonutils.NewString("controlplane")) + } + if o.Worker { + roles.Add(jsonutils.NewString("worker")) + } + } + params.Add(roles, "roles") + if o.HostnameOverride != "" { + params.Add(jsonutils.NewString(o.HostnameOverride), "hostname_override") + } + if o.Host != "" { + params.Add(jsonutils.NewString(o.Host), "host") + } + return params +} diff --git a/pkg/mcclient/options/k8s/release.go b/pkg/mcclient/options/k8s/release.go new file mode 100644 index 0000000000..a63bc601cc --- /dev/null +++ b/pkg/mcclient/options/k8s/release.go @@ -0,0 +1,136 @@ +package k8s + +import ( + "io/ioutil" + + "yunion.io/x/jsonutils" +) + +type ReleaseListOptions struct { + NamespaceResourceListOptions + Name string `help:"Search by name"` + Filter string `help:"Filter, split by space"` + Admin bool `help:"Admin to show all namespace releases"` + Deployed bool `help:"Show deployed status releases"` + Deleted bool `help:"Show deleted status releases"` + Deleting bool `help:"Show deleting status releases"` + Failed bool `help:"Show failed status releases"` + Superseded bool `help:"Show superseded status releases"` + Pending bool `help:"Show pending status releases"` +} + +func (o ReleaseListOptions) Params() *jsonutils.JSONDict { + params := o.NamespaceResourceListOptions.Params() + if o.Filter != "" { + params.Add(jsonutils.NewString(o.Filter), "filter") + } + if o.Namespace != "" { + params.Add(jsonutils.NewString(o.Namespace), "namespace") + } + if o.Name != "" { + params.Add(jsonutils.NewString(o.Name), "name") + } + params.Add(jsonutils.JSONTrue, "all") + if o.Admin { + params.Add(jsonutils.JSONTrue, "admin") + } + if o.Deployed { + params.Add(jsonutils.JSONTrue, "deployed") + } + if o.Deleted { + params.Add(jsonutils.JSONTrue, "deleted") + } + if o.Deleting { + params.Add(jsonutils.JSONTrue, "deleting") + } + if o.Failed { + params.Add(jsonutils.JSONTrue, "failed") + } + if o.Superseded { + params.Add(jsonutils.JSONTrue, "superseded") + } + if o.Pending { + params.Add(jsonutils.JSONTrue, "pending") + } + return params +} + +type ReleaseCreateUpdateOptions struct { + Values string `help:"Specify values in a YAML file (can specify multiple)" short-token:"f"` + Version string `help:"Specify the exact chart version to install. If not specified, latest version installed"` + //Set []string `help:"set STRING values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)"` + DryRun bool `help:"Simulate an install"` + Details bool `help:"Show release deploy details, include kubernetes created resources"` + Timeout int64 `help:"Time in seconds to wait for any individual kubernetes operation (like Jobs for hooks)" default:"600"` +} + +func (o ReleaseCreateUpdateOptions) Params() (*jsonutils.JSONDict, error) { + params := jsonutils.NewDict() + if o.Version != "" { + params.Add(jsonutils.NewString(o.Version), "version") + } + if o.DryRun { + params.Add(jsonutils.JSONTrue, "dry_run") + } + params.Add(jsonutils.NewInt(o.Timeout), "timeout") + if o.Values != "" { + //vals, err := helm.MergeValuesF(args.Values, args.Set, []string{}) + vals, err := ioutil.ReadFile(o.Values) + if err != nil { + return nil, err + } + params.Add(jsonutils.NewString(string(vals)), "values") + } + return params, nil +} + +type ReleaseCreateOptions struct { + NamespaceWithClusterOptions + ReleaseCreateUpdateOptions + Name string `help:"Release name, If unspecified, it will autogenerate one for you"` + CHARTNAME string `help:"Helm chart name, e.g stable/etcd"` +} + +func (o ReleaseCreateOptions) Params() (*jsonutils.JSONDict, error) { + params, err := o.ReleaseCreateUpdateOptions.Params() + if err != nil { + return nil, err + } + params.Update(o.NamespaceWithClusterOptions.Params()) + params.Add(jsonutils.NewString(o.CHARTNAME), "chart_name") + if o.Name != "" { + params.Add(jsonutils.NewString(o.Name), "release_name") + } + return params, nil +} + +type ReleaseUpgradeOptions struct { + ClusterBaseOptions + ReleaseCreateUpdateOptions + NAME string `help:"Release instance name"` + CHARTNAME string `help:"Helm chart name, e.g stable/etcd"` + ReuseValues bool `help:"When upgrading, reuse the last release's values, and merge in any new values. If '--reset-values' is specified, this is ignored"` + ResetValues bool `help:"When upgrading, reset the values to the ones built into the chart"` +} + +func (o ReleaseUpgradeOptions) Params() (*jsonutils.JSONDict, error) { + params, err := o.ReleaseCreateUpdateOptions.Params() + if err != nil { + return nil, err + } + params.Update(o.ClusterBaseOptions.Params()) + params.Add(jsonutils.NewString(o.CHARTNAME), "chart_name") + params.Add(jsonutils.NewString(o.NAME), "release_name") + if o.ReuseValues { + params.Add(jsonutils.JSONTrue, "reuse_values") + } + if o.ResetValues { + params.Add(jsonutils.JSONTrue, "reset_values") + } + return params, nil +} + +type ReleaseDeleteOptions struct { + ClusterBaseOptions + NAME string `help:"Release instance name"` +} diff --git a/pkg/mcclient/options/k8s/repo.go b/pkg/mcclient/options/k8s/repo.go new file mode 100644 index 0000000000..5c42277134 --- /dev/null +++ b/pkg/mcclient/options/k8s/repo.go @@ -0,0 +1,44 @@ +package k8s + +import ( + "yunion.io/x/jsonutils" + + "yunion.io/x/onecloud/pkg/mcclient/options" +) + +type RepoListOptions struct { + options.BaseListOptions +} + +type RepoGetOptions struct { + NAME string `help:"ID or name of the repo"` +} + +type RepoCreateOptions struct { + RepoGetOptions + URL string `help:"Repository url"` +} + +func (o RepoCreateOptions) Params() *jsonutils.JSONDict { + params := jsonutils.NewDict() + params.Add(jsonutils.NewString(o.NAME), "name") + params.Add(jsonutils.NewString(o.URL), "url") + return params +} + +type RepoUpdateOptions struct { + RepoGetOptions + Name string `help:"Repository name to change"` + Url string `help:"Repository url to change"` +} + +func (o RepoUpdateOptions) Params() *jsonutils.JSONDict { + params := jsonutils.NewDict() + if o.Name != "" { + params.Add(jsonutils.NewString(o.Name), "name") + } + if o.Url != "" { + params.Add(jsonutils.NewString(o.Url), "url") + } + return params +} diff --git a/pkg/mcclient/options/k8s/tiller.go b/pkg/mcclient/options/k8s/tiller.go new file mode 100644 index 0000000000..a1a6a64552 --- /dev/null +++ b/pkg/mcclient/options/k8s/tiller.go @@ -0,0 +1,44 @@ +package k8s + +import ( + "yunion.io/x/jsonutils" +) + +type TillerCreateOptions struct { + ClusterBaseOptions + KubeContext string `json:"kube_context"` + Namespace string `json:"namespace" default:"kube-system"` + // Upgrade if Tiller is already installed + Upgrade bool `json:"upgrade"` + // Name of service account + ServiceAccount string `json:"service_account" default:"tiller"` + // Use the canary Tiller image + Canary bool `json:"canary_image"` + + // Override Tiller image + Image string `json:"tiller_image" default:"yunion/tiller:v2.9.0"` + // Limit the maximum number of revisions saved per release. Use 0 for no limit. + MaxHistory int `json:"history_max"` +} + +func (o TillerCreateOptions) Params() *jsonutils.JSONDict { + params := o.ClusterBaseOptions.Params() + if len(o.KubeContext) > 0 { + params.Add(jsonutils.NewString(o.KubeContext), "kube_context") + } + params.Add(jsonutils.NewString(o.Namespace), "namespace") + params.Add(jsonutils.NewString(o.ServiceAccount), "service_account") + if o.Canary { + params.Add(jsonutils.JSONTrue, "canary_image") + } + if o.Upgrade { + params.Add(jsonutils.JSONTrue, "upgrade") + } + if len(o.Image) > 0 { + params.Add(jsonutils.NewString(o.Image), "tiller_image") + } + if o.MaxHistory > 0 { + params.Add(jsonutils.NewInt(int64(o.MaxHistory)), "history_max") + } + return params +} From f7769ef1e3ab0c6a547a3be61d8c7ed68be5ddbd Mon Sep 17 00:00:00 2001 From: Qiu Jian Date: Fri, 14 Sep 2018 01:15:41 +0800 Subject: [PATCH 2/9] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=EF=BC=9A1.=20=E9=98=BF?= =?UTF-8?q?=E9=87=8C=E4=BA=91rebuild=20root=E9=87=8D=E6=9E=84=202.=20?= =?UTF-8?q?=E5=85=B6=E4=BB=96=E4=B8=80=E4=BA=9Bfix?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pkg/cloudcommon/db/quotas/quotas.go | 12 +- pkg/cloudcommon/db/standalone.go | 2 +- pkg/cloudcommon/db/taskman/tasks.go | 2 +- pkg/cloudcommon/db/virtualjointbase.go | 27 ++- pkg/cloudprovider/resources.go | 11 +- pkg/compute/guestdrivers/aliyun.go | 190 +++++++++++++++---- pkg/compute/guestdrivers/kvm.go | 2 +- pkg/compute/guestdrivers/managedvirtual.go | 9 + pkg/compute/guestdrivers/virtualization.go | 1 + pkg/compute/models/disks.go | 3 + pkg/compute/models/hosts.go | 31 ++- pkg/compute/models/networks.go | 4 + pkg/compute/models/quotas.go | 29 +-- pkg/compute/tasks/eip_deallocate_task.go | 27 +-- pkg/compute/tasks/guest_insert_iso_task.go | 13 +- pkg/compute/tasks/guest_rebuild_root_task.go | 49 ++++- pkg/util/aliyun/host.go | 82 +++----- pkg/util/aliyun/image.go | 2 +- pkg/util/aliyun/instance.go | 81 +++++--- pkg/util/aliyun/keypair.go | 42 ++++ pkg/util/aliyun/region.go | 2 +- pkg/util/aliyun/shell/instance.go | 10 +- pkg/util/aliyun/vpc.go | 5 +- pkg/util/esxi/host.go | 2 +- pkg/util/esxi/virtualmachine.go | 6 +- pkg/util/logclient/logclient.go | 25 ++- 26 files changed, 476 insertions(+), 193 deletions(-) diff --git a/pkg/cloudcommon/db/quotas/quotas.go b/pkg/cloudcommon/db/quotas/quotas.go index b80fb3c1d0..4ff3f164aa 100644 --- a/pkg/cloudcommon/db/quotas/quotas.go +++ b/pkg/cloudcommon/db/quotas/quotas.go @@ -21,7 +21,7 @@ type IQuota interface { Update(quota IQuota) Add(quota IQuota) Sub(quota IQuota) - Exceed(quota IQuota) error + Exceed(request IQuota, quota IQuota) error IsEmpty() bool ToJSON(prefix string) jsonutils.JSONObject } @@ -101,14 +101,14 @@ func (manager *SQuotaManager) _setQuota(ctx context.Context, userCred mcclient.T return manager.persistenStore.SetQuota(ctx, userCred, projectId, quota) } -func (manager *SQuotaManager) CheckQuota(ctx context.Context, userCred mcclient.TokenCredential, projectId string, quota IQuota) (IQuota, error) { +func (manager *SQuotaManager) CheckQuota(ctx context.Context, userCred mcclient.TokenCredential, projectId string, request IQuota) (IQuota, error) { lockman.LockClass(ctx, manager, projectId) defer lockman.ReleaseClass(ctx, manager, projectId) - return manager._checkQuota(ctx, userCred, projectId, quota) + return manager._checkQuota(ctx, userCred, projectId, request) } -func (manager *SQuotaManager) _checkQuota(ctx context.Context, userCred mcclient.TokenCredential, projectId string, quota IQuota) (IQuota, error) { +func (manager *SQuotaManager) _checkQuota(ctx context.Context, userCred mcclient.TokenCredential, projectId string, request IQuota) (IQuota, error) { stored := manager.newQuota() err := manager.GetQuota(ctx, projectId, stored) if err != nil { @@ -130,9 +130,9 @@ func (manager *SQuotaManager) _checkQuota(ctx context.Context, userCred mcclient } used.Add(pending) - used.Add(quota) + used.Add(request) - err = used.Exceed(stored) + err = used.Exceed(request, stored) if err != nil { return nil, err } diff --git a/pkg/cloudcommon/db/standalone.go b/pkg/cloudcommon/db/standalone.go index 7cb0c94fa5..d39d6b71db 100644 --- a/pkg/cloudcommon/db/standalone.go +++ b/pkg/cloudcommon/db/standalone.go @@ -20,7 +20,7 @@ type SStandaloneResourceBase struct { Id string `width:"128" charset:"ascii" primary:"true" list:"user"` Name string `width:"128" charset:"utf8" nullable:"false" index:"true" list:"user" update:"user" create:"required"` - ExternalId string `width:"128" charset:"ascii" index:"true" get:"admin" create:"admin_optional"` + ExternalId string `width:"128" charset:"ascii" index:"true" list:"admin" create:"admin_optional"` Description string `width:"256" charset:"utf8" get:"user" update:"user" create:"optional"` diff --git a/pkg/cloudcommon/db/taskman/tasks.go b/pkg/cloudcommon/db/taskman/tasks.go index f221ae6f11..6fe0571536 100644 --- a/pkg/cloudcommon/db/taskman/tasks.go +++ b/pkg/cloudcommon/db/taskman/tasks.go @@ -402,7 +402,7 @@ func execITask(taskValue reflect.Value, task *STask, odata jsonutils.JSONObject, params[2] = reflect.ValueOf(data) - log.Debugf("Call %s: %s with %s", stageName, funcValue, params) + log.Debugf("Call %s %s: %s with %s", task.TaskName, stageName, funcValue, params) funcValue.Call(params) diff --git a/pkg/cloudcommon/db/virtualjointbase.go b/pkg/cloudcommon/db/virtualjointbase.go index e4a0d941c0..cec26d938f 100644 --- a/pkg/cloudcommon/db/virtualjointbase.go +++ b/pkg/cloudcommon/db/virtualjointbase.go @@ -1,16 +1,17 @@ package db import ( - "context" "fmt" + "context" "reflect" - "yunion.io/x/jsonutils" "yunion.io/x/log" - "yunion.io/x/onecloud/pkg/httperrors" - "yunion.io/x/onecloud/pkg/mcclient" - "yunion.io/x/pkg/util/reflectutils" + "yunion.io/x/jsonutils" "yunion.io/x/sqlchemy" + "yunion.io/x/pkg/util/reflectutils" + + "yunion.io/x/onecloud/pkg/mcclient" + "yunion.io/x/onecloud/pkg/httperrors" ) type SVirtualJointResourceBase struct { @@ -59,6 +60,22 @@ func (manager *SVirtualJointResourceBaseManager) AllowAttach(ctx context.Context return false } +/*func (joint *SVirtualJointResourceBase) GetJointModelManager() IJointModelManager { + +}*/ + +func (joint *SVirtualJointResourceBase) Detach(ctx context.Context, userCred mcclient.TokenCredential) error { + return nil +} + +func (joint *SVirtualJointResourceBase) Master() IStandaloneModel { + return JointMaster(joint) +} + +func (joint *SVirtualJointResourceBase) Slave() IStandaloneModel { + return JointSlave(joint) +} + func (self *SVirtualJointResourceBase) AllowGetDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool { masterVirtual := self.Master().(IVirtualModel) return masterVirtual.IsOwner(userCred) diff --git a/pkg/cloudprovider/resources.go b/pkg/cloudprovider/resources.go index 0b29100c9b..323aaec730 100644 --- a/pkg/cloudprovider/resources.go +++ b/pkg/cloudprovider/resources.go @@ -128,7 +128,7 @@ type ICloudHost interface { GetManagerId() string CreateVM(name string, imgId string, sysDiskSize int, cpu int, memMB int, vswitchId string, ipAddr string, desc string, - passwd string, storageType string, diskSizes []int, publicKey string) (ICloudVM, error) + passwd string, storageType string, diskSizes []int, publicKey string, extSecGrpId string) (ICloudVM, error) } type ICloudVM interface { @@ -165,8 +165,11 @@ type ICloudVM interface { DeleteVM() error UpdateVM(name string) error - RebuildRoot(imageId string) error - DeployVM(name string, password string, publicKey string, resetPassword bool, deleteKeypair bool, description string) error + + RebuildRoot(imageId string, passwd string, publicKey string, sysSizeGB int) (string, error) + + DeployVM(name string, password string, publicKey string, deleteKeypair bool, description string) error + ChangeConfig(instanceId string, ncpu int, vmem int) error GetVNCInfo() (jsonutils.JSONObject, error) AttachDisk(diskId string) error @@ -254,6 +257,8 @@ type ICloudVpc interface { Delete() error GetIWireById(wireId string) (ICloudWire, error) + + SyncSecurityGroup(secgroupId string, name string, rules []secrules.SecurityRule) (string, error) } type ICloudWire interface { diff --git a/pkg/compute/guestdrivers/aliyun.go b/pkg/compute/guestdrivers/aliyun.go index a9f84fce48..65dadac6ea 100644 --- a/pkg/compute/guestdrivers/aliyun.go +++ b/pkg/compute/guestdrivers/aliyun.go @@ -17,6 +17,7 @@ import ( "yunion.io/x/onecloud/pkg/cloudprovider" "yunion.io/x/onecloud/pkg/compute/models" "yunion.io/x/onecloud/pkg/util/seclib2" + "yunion.io/x/pkg/util/secrules" ) type SAliyunGuestDriver struct { @@ -33,7 +34,7 @@ func (self *SAliyunGuestDriver) GetHypervisor() string { } func (self *SAliyunGuestDriver) ChooseHostStorage(host *models.SHost, backend string) *models.SStorage { - storages := host.GetAttachedStorages() + storages := host.GetAttachedStorages("") for i := 0; i < len(storages); i += 1 { if storages[i].StorageType == backend { return &storages[i] @@ -82,6 +83,9 @@ type SAliyunVMCreateConfig struct { SysDiskSize int DataDisks []int PublicKey string + SecGroupId string + SecGroupName string + SecRules []secrules.SecurityRule } func (self *SAliyunGuestDriver) GetJsonDescAtHost(ctx context.Context, guest *models.SGuest, host *models.SHost) jsonutils.JSONObject { @@ -100,6 +104,10 @@ func (self *SAliyunGuestDriver) GetJsonDescAtHost(ctx context.Context, guest *mo config.ExternalNetworkId = net.ExternalId config.IpAddr = nics[0].IpAddr + config.SecGroupId = guest.SecgrpId + config.SecGroupName = guest.GetSecgroupName() + config.SecRules = guest.GetSecRules() + disks := guest.GetDisks() config.DataDisks = make([]int, len(disks)-1) @@ -130,6 +138,54 @@ type SDiskInfo struct { Metadata map[string]string } +func fetchIVMinfo(desc SAliyunVMCreateConfig, iVM cloudprovider.ICloudVM, guestId string, passwd string) *jsonutils.JSONDict { + data := jsonutils.NewDict() + + data.Add(jsonutils.NewString(iVM.GetOSType()), "os") + + if len(passwd) > 0 { + encpasswd, err := utils.EncryptAESBase64(guestId, passwd) + if err != nil { + log.Errorf("encrypt password failed %s", err) + } + data.Add(jsonutils.NewString("root"), "account") + data.Add(jsonutils.NewString(encpasswd), "key") + } + + if len(desc.OsDistribution) > 0 { + data.Add(jsonutils.NewString(desc.OsDistribution), "distro") + } + if len(desc.OsVersion) > 0 { + data.Add(jsonutils.NewString(desc.OsVersion), "version") + } + + idisks, err := iVM.GetIDisks() + + if err != nil { + log.Errorf("GetiDisks error %s", err) + } else { + diskInfo := make([]SDiskInfo, len(idisks)) + for i := 0; i < len(idisks); i += 1 { + dinfo := SDiskInfo{} + dinfo.Uuid = idisks[i].GetGlobalId() + dinfo.Size = idisks[i].GetDiskSizeMB() + if metaData := idisks[i].GetMetadata(); metaData != nil { + dinfo.Metadata = make(map[string]string, 0) + if err := metaData.Unmarshal(dinfo.Metadata); err != nil { + log.Errorf("Get disk %s metadata info error: %v", idisks[i].GetName(), err) + } + } + diskInfo[i] = dinfo + } + data.Add(jsonutils.Marshal(&diskInfo), "disks") + } + + data.Add(jsonutils.NewString(iVM.GetGlobalId()), "uuid") + data.Add(iVM.GetMetadata(), "metadata") + + return data +} + func (self *SAliyunGuestDriver) RequestDeployGuestOnHost(ctx context.Context, guest *models.SGuest, host *models.SHost, task taskman.ITask) error { config := guest.GetDeployConfigOnHost(ctx, host, task.GetParams()) @@ -143,23 +199,46 @@ func (self *SAliyunGuestDriver) RequestDeployGuestOnHost(ctx context.Context, gu return err } + publicKey, _ := config.GetString("public_key") + + resetPassword := jsonutils.QueryBoolean(config, "reset_password", false) + passwd, _ := config.GetString("password") + if resetPassword && len(passwd) == 0 { + passwd = seclib2.RandomPassword2(12) + } + ihost, err := host.GetIHost() if err != nil { return err } - if action == "create" { - desc := SAliyunVMCreateConfig{} - err = config.Unmarshal(&desc, "desc") - if err != nil { - return err - } + desc := SAliyunVMCreateConfig{} + err = config.Unmarshal(&desc, "desc") + if err != nil { + return err + } + if action == "create" { taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) { - passwd := seclib2.RandomPassword2(12) + + nets := guest.GetNetworks() + net := nets[0].GetNetwork() + vpc := net.GetVpc() + + ivpc, err := vpc.GetIVpc() + if err != nil { + log.Errorf("getIVPC fail %s", err) + return nil, err + } + + secgrpId, err := ivpc.SyncSecurityGroup(desc.SecGroupId, desc.SecGroupName, desc.SecRules) + if err != nil { + log.Errorf("SyncSecurityGroup fail %s", err) + return nil, err + } iVM, err := ihost.CreateVM(desc.Name, desc.ExternalImageId, desc.SysDiskSize, desc.Cpu, desc.Memory, desc.ExternalNetworkId, - desc.IpAddr, desc.Description, passwd, desc.StorageType, desc.DataDisks, desc.PublicKey) + desc.IpAddr, desc.Description, passwd, desc.StorageType, desc.DataDisks, publicKey, secgrpId) if err != nil { return nil, err } @@ -176,12 +255,12 @@ func (self *SAliyunGuestDriver) RequestDeployGuestOnHost(ctx context.Context, gu return nil, err } - if len(guest.SecgrpId) > 0 { + /*if len(guest.SecgrpId) > 0 { if err := iVM.SyncSecurityGroup(guest.SecgrpId, guest.GetSecgroupName(), guest.GetSecRules()); err != nil { log.Errorf("SyncSecurityGroup error: %v", err) return nil, err } - } + }*/ /*if onfinish == "none" { err = iVM.StartVM() @@ -190,15 +269,18 @@ func (self *SAliyunGuestDriver) RequestDeployGuestOnHost(ctx context.Context, gu } }*/ - encpasswd, err := utils.EncryptAESBase64(guest.Id, passwd) - if err != nil { - log.Errorf("encrypt password failed %s", err) - } + data := fetchIVMinfo(desc, iVM, guest.Id, passwd) - data := jsonutils.NewDict() - data.Add(jsonutils.NewString(iVM.GetOSType()), "os") - data.Add(jsonutils.NewString("root"), "account") - data.Add(jsonutils.NewString(encpasswd), "key") + /* data.Add(jsonutils.NewString(iVM.GetOSType()), "os") + + if len(passwd) > 0 { + encpasswd, err := utils.EncryptAESBase64(guest.Id, passwd) + if err != nil { + log.Errorf("encrypt password failed %s", err) + } + data.Add(jsonutils.NewString("root"), "account") + data.Add(jsonutils.NewString(encpasswd), "key") + } if len(desc.OsDistribution) > 0 { data.Add(jsonutils.NewString(desc.OsDistribution), "distro") @@ -230,6 +312,7 @@ func (self *SAliyunGuestDriver) RequestDeployGuestOnHost(ctx context.Context, gu data.Add(jsonutils.NewString(iVM.GetGlobalId()), "uuid") data.Add(iVM.GetMetadata(), "metadata") + */ return data, nil }) @@ -250,30 +333,71 @@ func (self *SAliyunGuestDriver) RequestDeployGuestOnHost(ctx context.Context, gu if v, e := params.GetString("description"); e != nil { description = v } - resetPassword := jsonutils.QueryBoolean(params, "reset_password", false) + //resetPassword := jsonutils.QueryBoolean(params, "reset_password", false) deleteKeypair := jsonutils.QueryBoolean(params, "__delete_keypair__", false) - password, _ := params.GetString("password") - if resetPassword && len(password) == 0 { - password = seclib2.RandomPassword2(12) - } + //password, _ := params.GetString("password") + //if resetPassword && len(password) == 0 { + // password = seclib2.RandomPassword2(12) + //} + /* publicKey := "" if k, e := config.GetString("public_key"); e == nil { publicKey = k - } + }*/ + taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) { - encpasswd, err := utils.EncryptAESBase64(guest.Id, password) + + err := iVM.DeployVM(name, passwd, publicKey, deleteKeypair, description) if err != nil { - log.Errorf("encrypt password failed %s", err) + return nil, err } + data := fetchIVMinfo(desc, iVM, guest.Id, passwd) + + /* data := jsonutils.NewDict() - data.Add(jsonutils.NewString("root"), "account") // 用户名 - data.Add(jsonutils.NewString(encpasswd), "key") // 密码 - e := iVM.DeployVM(name, password, publicKey, resetPassword, deleteKeypair, description) - return data, e + + if len(passwd) > 0 { + encpasswd, err := utils.EncryptAESBase64(guest.Id, passwd) + if err != nil { + log.Errorf("encrypt password failed %s", err) + } + + + data.Add(jsonutils.NewString("root"), "account") // 用户名 + data.Add(jsonutils.NewString(encpasswd), "key") // 密码 + }*/ + + return data, nil }) + } else if action == "rebuild" { + iVM, err := ihost.GetIVMById(guest.GetExternalId()) + if err != nil || iVM == nil { + log.Errorf("cannot find vm %s", err) + return fmt.Errorf("cannot find vm") + } + + taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) { + _, err := iVM.RebuildRoot(desc.ExternalImageId, passwd, publicKey, desc.SysDiskSize) + if err != nil { + return nil, err + } + + log.Debugf("VMrebuildRoot %s, wait status ready ...", iVM.GetGlobalId()) + err = cloudprovider.WaitStatus(iVM, models.VM_READY, time.Second*5, time.Second*1800) + if err != nil { + return nil, err + } + log.Debugf("VMrebuildRoot %s, and status is ready", iVM.GetGlobalId()) + + data := fetchIVMinfo(desc, iVM, guest.Id, passwd) + + return data, nil + }) + } else { + log.Errorf("RequestDeployGuestOnHost: Action %s not supported", action) return fmt.Errorf("Action %s not supported", action) } @@ -462,7 +586,7 @@ func (self *SAliyunGuestDriver) RequestStartOnHost(ctx context.Context, guest *m return result, e } -func (self *SAliyunGuestDriver) RequestRebuildRootDisk(ctx context.Context, guest *models.SGuest, task taskman.ITask) error { +/*func (self *SAliyunGuestDriver) RequestRebuildRootDisk(ctx context.Context, guest *models.SGuest, task taskman.ITask) error { ihost, e := guest.GetHost().GetIHost() if e != nil { return e @@ -504,4 +628,4 @@ func (self *SAliyunGuestDriver) RequestRebuildRootDisk(ctx context.Context, gues task.ScheduleRun(nil) return nil -} +}*/ diff --git a/pkg/compute/guestdrivers/kvm.go b/pkg/compute/guestdrivers/kvm.go index 0c3e1ab525..a490c82afd 100644 --- a/pkg/compute/guestdrivers/kvm.go +++ b/pkg/compute/guestdrivers/kvm.go @@ -261,7 +261,7 @@ func (self *SKVMGuestDriver) RequestGuestHotAddIso(ctx context.Context, guest *m } func (self *SKVMGuestDriver) RequestRebuildRootDisk(ctx context.Context, guest *models.SGuest, task taskman.ITask) error { - subtask, err := taskman.TaskManager.NewTask(ctx, "KVMGuestRebuildRootTask", guest, task.GetUserCred(), nil, task.GetTaskId(), "", nil) + subtask, err := taskman.TaskManager.NewTask(ctx, "KVMGuestRebuildRootTask", guest, task.GetUserCred(), task.GetParams(), task.GetTaskId(), "", nil) if err != nil { return err } diff --git a/pkg/compute/guestdrivers/managedvirtual.go b/pkg/compute/guestdrivers/managedvirtual.go index 5976b6ff60..0a5901a783 100644 --- a/pkg/compute/guestdrivers/managedvirtual.go +++ b/pkg/compute/guestdrivers/managedvirtual.go @@ -152,3 +152,12 @@ func (self *SManagedVirtualizedGuestDriver) GetGuestVncInfo(userCred mcclient.To return dataDict, nil } + +func (self *SManagedVirtualizedGuestDriver) RequestRebuildRootDisk(ctx context.Context, guest *models.SGuest, task taskman.ITask) error { + subtask, err := taskman.TaskManager.NewTask(ctx, "ManagedGuestRebuildRootTask", guest, task.GetUserCred(), nil, task.GetTaskId(), "", nil) + if err != nil { + return err + } + subtask.ScheduleRun(nil) + return nil +} \ No newline at end of file diff --git a/pkg/compute/guestdrivers/virtualization.go b/pkg/compute/guestdrivers/virtualization.go index a9ef20f3f1..e665f3ac18 100644 --- a/pkg/compute/guestdrivers/virtualization.go +++ b/pkg/compute/guestdrivers/virtualization.go @@ -49,6 +49,7 @@ func (self *SVirtualizedGuestDriver) Attach2RandomNetwork(guest *models.SGuest, wire := hostwire.GetWire() if wire == nil { + log.Errorf("host wire is nil?????") continue } diff --git a/pkg/compute/models/disks.go b/pkg/compute/models/disks.go index 25eabb4921..7c4649f67b 100644 --- a/pkg/compute/models/disks.go +++ b/pkg/compute/models/disks.go @@ -751,6 +751,9 @@ func parseDiskInfo(ctx context.Context, userCred mcclient.TokenCredential, info } parts := strings.Split(diskStr, ":") for _, p := range parts { + if len(p) == 0 { + continue + } if regutils.MatchSize(p) { diskConfig.Size, _ = fileutils.GetSizeMb(p, 'M', 1024) } else if utils.IsInStringArray(p, osprofile.FS_TYPES) { diff --git a/pkg/compute/models/hosts.go b/pkg/compute/models/hosts.go index b050c9b042..77ef03324a 100644 --- a/pkg/compute/models/hosts.go +++ b/pkg/compute/models/hosts.go @@ -18,7 +18,6 @@ import ( "yunion.io/x/sqlchemy" "yunion.io/x/onecloud/pkg/cloudcommon/db" - "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman" "yunion.io/x/onecloud/pkg/cloudprovider" "yunion.io/x/onecloud/pkg/compute/options" "yunion.io/x/onecloud/pkg/httperrors" @@ -437,11 +436,11 @@ func (self *SHost) GetFetchUrl() string { return fmt.Sprintf("%s://%s:%d", managerUrl.Scheme, managerUrl.Host, port+40000) } -func (self *SHost) GetAttachedStorages() []SStorage { - return self._getAttachedStorages(tristate.False, tristate.True) +func (self *SHost) GetAttachedStorages(storageType string) []SStorage { + return self._getAttachedStorages(tristate.False, tristate.True, storageType) } -func (self *SHost) _getAttachedStorages(isBaremetal tristate.TriState, enabled tristate.TriState) []SStorage { +func (self *SHost) _getAttachedStorages(isBaremetal tristate.TriState, enabled tristate.TriState, storageType string) []SStorage { storages := StorageManager.Query().SubQuery() hoststorages := HoststorageManager.Query().SubQuery() q := storages.Query() @@ -456,6 +455,9 @@ func (self *SHost) _getAttachedStorages(isBaremetal tristate.TriState, enabled t } else if isBaremetal.IsFalse() { q = q.NotEquals("storage_type", STORAGE_BAREMETAL) } + if len(storageType) > 0 { + q = q.Equals("storage_type", storageType) + } q = q.Filter(sqlchemy.Equals(hoststorages.Field("host_id"), self.Id)) ret := make([]SStorage, 0) err := db.FetchModelObjects(StorageManager, q, &ret) @@ -467,7 +469,7 @@ func (self *SHost) _getAttachedStorages(isBaremetal tristate.TriState, enabled t } func (self *SHost) SyncAttachedStorageStatus() { - storages := self.GetAttachedStorages() + storages := self.GetAttachedStorages("") if storages != nil { for _, storage := range storages { storage.SyncStatusWithHosts() @@ -567,7 +569,7 @@ type SStorageCapacity struct { func (self *SHost) GetAttachedStorageCapacity() SStorageCapacity { ret := SStorageCapacity{} - storages := self.GetAttachedStorages() + storages := self.GetAttachedStorages("") if storages != nil { for _, s := range storages { ret.Capacity += s.GetCapacity() @@ -612,7 +614,7 @@ func getLeastUsedStorage(storages []SStorage, backend string) *SStorage { } func (self *SHost) GetLeastUsedStorage(backend string) *SStorage { - storages := self.GetAttachedStorages() + storages := self.GetAttachedStorages("") if storages != nil { return getLeastUsedStorage(storages, backend) } @@ -1607,7 +1609,7 @@ func (manager *SHostManager) GetHostsByManagerAndRegion(managerId string, region return ret } -func (self *SHost) StartImageCacheTask(ctx context.Context, userCred mcclient.TokenCredential, imageId, parentTaskId string, isForce bool) error { +/*func (self *SHost) StartImageCacheTask(ctx context.Context, userCred mcclient.TokenCredential, imageId, parentTaskId string, isForce bool) error { //Todo // HostcachedimagesManager.Register(userCred, self, imageId) data := jsonutils.NewDict() @@ -1621,10 +1623,21 @@ func (self *SHost) StartImageCacheTask(ctx context.Context, userCred mcclient.To } task.ScheduleRun(nil) return nil -} +}*/ func (self *SHost) Request(userCred mcclient.TokenCredential, method string, url string, headers http.Header, body jsonutils.JSONObject) (jsonutils.JSONObject, error) { s := auth.GetSession(userCred, "", "") _, ret, err := s.JSONRequest(self.ManagerUri, "", method, url, headers, body) return ret, err } + +func (self *SHost) GetLocalStoragecache() *SStoragecache { + localStorages := self.GetAttachedStorages(STORAGE_LOCAL) + for i := 0; i < len(localStorages); i += 1 { + sc := localStorages[i].GetStoragecache() + if sc != nil { + return sc + } + } + return nil +} \ No newline at end of file diff --git a/pkg/compute/models/networks.go b/pkg/compute/models/networks.go index bf8a79c0f4..da8954108c 100644 --- a/pkg/compute/models/networks.go +++ b/pkg/compute/models/networks.go @@ -230,6 +230,7 @@ func (self *SNetwork) getFreeIP(addrTable map[string]bool, candidate string, all if _, ok := addrTable[ip.String()]; !ok { return ip.String(), nil } + ip = ip.StepUp() } } return "", httperrors.NewInsufficientResourceError("Out of IP address") @@ -630,6 +631,9 @@ func parseNetworkInfo(userCred mcclient.TokenCredential, info jsonutils.JSONObje } parts := strings.Split(netStr, ":") for _, p := range parts { + if len(p) == 0 { + continue + } if regutils.MatchIP4Addr(p) { netConfig.Address = p } else if regutils.MatchIP6Addr(p) { diff --git a/pkg/compute/models/quotas.go b/pkg/compute/models/quotas.go index f18d91ce89..8634b9c7a7 100644 --- a/pkg/compute/models/quotas.go +++ b/pkg/compute/models/quotas.go @@ -221,45 +221,46 @@ func (self *SQuota) Update(quota quotas.IQuota) { } } -func (self *SQuota) Exceed(quota quotas.IQuota) error { +func (self *SQuota) Exceed(request quotas.IQuota, quota quotas.IQuota) error { + sreq := request.(*SQuota) squota := quota.(*SQuota) - if self.Cpu > squota.Cpu { + if sreq.Cpu > 0 && self.Cpu > squota.Cpu { return ErrOutOfCPU } - if self.Memory > squota.Memory { + if sreq.Memory > 0 && self.Memory > squota.Memory { return ErrOutOfMemory } - if self.Storage > squota.Storage { + if sreq.Storage > 0 && self.Storage > squota.Storage { return ErrOutOfStorage } - if self.Port > squota.Port { + if sreq.Port > 0 && self.Port > squota.Port { return ErrOutOfPort } - if self.Eip > squota.Eip { + if sreq.Eip > 0 && self.Eip > squota.Eip { return ErrOutOfEip } - if self.Eport > squota.Eport { + if sreq.Eport > 0 && self.Eport > squota.Eport { return ErrOutOfEport } - if self.Bw > squota.Bw { + if sreq.Bw > 0 && self.Bw > squota.Bw { return ErrOutOfBw } - if self.Ebw > squota.Ebw { + if sreq.Ebw > 0 && self.Ebw > squota.Ebw { return ErrOutOfEbw } - if self.Keypair > squota.Keypair { + if sreq.Keypair > 0 && self.Keypair > squota.Keypair { return ErrOutOfKeypair } - if self.Image > squota.Image { + if sreq.Image > 0 && self.Image > squota.Image { return ErrOutOfImage } - if self.Group > squota.Group { + if sreq.Group > 0 && self.Group > squota.Group { return ErrOutOfGroup } - if self.Secgroup > squota.Secgroup { + if sreq.Secgroup > 0 && self.Secgroup > squota.Secgroup { return ErrOutOfSecgroup } - if self.IsolatedDevice > squota.IsolatedDevice { + if sreq.IsolatedDevice > 0 && self.IsolatedDevice > squota.IsolatedDevice { return ErrOutOfIsolatedDevice } return nil diff --git a/pkg/compute/tasks/eip_deallocate_task.go b/pkg/compute/tasks/eip_deallocate_task.go index 6efb5311e9..7497fcd07d 100644 --- a/pkg/compute/tasks/eip_deallocate_task.go +++ b/pkg/compute/tasks/eip_deallocate_task.go @@ -10,6 +10,7 @@ import ( "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman" "yunion.io/x/onecloud/pkg/compute/models" + "yunion.io/x/onecloud/pkg/cloudprovider" ) type EipDeallocateTask struct { @@ -26,18 +27,20 @@ func (self *EipDeallocateTask) OnInit(ctx context.Context, obj db.IStandaloneMod if len(eip.ExternalId) > 0 { expEip, err := eip.GetIEip() if err != nil { - msg := fmt.Sprintf("fail to find iEIP for eip %s", err) - eip.SetStatus(self.UserCred, models.EIP_STATUS_DEALLOCATE_FAIL, msg) - self.SetStageFailed(ctx, msg) - return - } - - err = expEip.Delete() - if err != nil { - msg := fmt.Sprintf("fail to delete iEIP %s", err) - eip.SetStatus(self.UserCred, models.EIP_STATUS_DEALLOCATE_FAIL, msg) - self.SetStageFailed(ctx, msg) - return + if err != cloudprovider.ErrNotFound { + msg := fmt.Sprintf("fail to find iEIP for eip %s", err) + eip.SetStatus(self.UserCred, models.EIP_STATUS_DEALLOCATE_FAIL, msg) + self.SetStageFailed(ctx, msg) + return + } + }else { + err = expEip.Delete() + if err != nil { + msg := fmt.Sprintf("fail to delete iEIP %s", err) + eip.SetStatus(self.UserCred, models.EIP_STATUS_DEALLOCATE_FAIL, msg) + self.SetStageFailed(ctx, msg) + return + } } } diff --git a/pkg/compute/tasks/guest_insert_iso_task.go b/pkg/compute/tasks/guest_insert_iso_task.go index e0b9816f52..babe5cc704 100644 --- a/pkg/compute/tasks/guest_insert_iso_task.go +++ b/pkg/compute/tasks/guest_insert_iso_task.go @@ -23,6 +23,7 @@ func (self *GuestInsertIsoTask) OnInit(ctx context.Context, obj db.IStandaloneMo } func (self *GuestInsertIsoTask) prepareIsoImage(ctx context.Context, obj db.IStandaloneModel) { + guest := obj.(*models.SGuest) imageId, _ := self.Params.GetString("image_id") db.OpsLog.LogEvent(obj, db.ACT_ISO_PREPARING, imageId, self.UserCred) var host *models.SHost @@ -31,11 +32,17 @@ func (self *GuestInsertIsoTask) prepareIsoImage(ctx context.Context, obj db.ISta iHost, _ := models.HostManager.FetchById(hostId) host = iHost.(*models.SHost) } else { - guest := obj.(*models.SGuest) host = guest.GetHost() } - self.SetStage("OnIsoPrepareComplete", nil) - host.StartImageCacheTask(ctx, self.UserCred, imageId, self.GetTaskId(), false) + storageCache := host.GetLocalStoragecache() + if storageCache != nil { + self.SetStage("OnIsoPrepareComplete", nil) + storageCache.StartImageCacheTask(ctx, self.UserCred, imageId, false, self.GetTaskId()) + } else { + guest.EjectIso(self.UserCred) + db.OpsLog.LogEvent(obj, db.ACT_ISO_PREPARE_FAIL, imageId, self.UserCred) + self.SetStageFailed(ctx, "host no local storage cache") + } } func (self *GuestInsertIsoTask) OnIsoPrepareCompleteFailed(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) { diff --git a/pkg/compute/tasks/guest_rebuild_root_task.go b/pkg/compute/tasks/guest_rebuild_root_task.go index a2a7e5c6fd..4202c450df 100644 --- a/pkg/compute/tasks/guest_rebuild_root_task.go +++ b/pkg/compute/tasks/guest_rebuild_root_task.go @@ -17,6 +17,7 @@ import ( func init() { taskman.RegisterTask(GuestRebuildRootTask{}) taskman.RegisterTask(KVMGuestRebuildRootTask{}) + taskman.RegisterTask(ManagedGuestRebuildRootTask{}) } type GuestRebuildRootTask struct { @@ -133,7 +134,9 @@ func (self *KVMGuestRebuildRootTask) OnInit(ctx context.Context, obj db.IStandal gds.Root.StartDiskCreateTask(ctx, self.UserCred, true, "", self.GetTaskId()) } -func (self *KVMGuestRebuildRootTask) OnRebuildRootDiskComplete(ctx context.Context, guest *models.SGuest, data jsonutils.JSONObject) { +func (self *KVMGuestRebuildRootTask) OnRebuildRootDiskComplete(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) { + guest := obj.(*models.SGuest) + self.SetStage("OnGuestDeployComplete", nil) guest.SetStatus(self.UserCred, models.VM_DEPLOYING, "") params := jsonutils.NewDict() @@ -141,12 +144,52 @@ func (self *KVMGuestRebuildRootTask) OnRebuildRootDiskComplete(ctx context.Conte guest.StartGuestDeployTask(ctx, self.UserCred, params, "deploy", self.GetTaskId()) } -func (self *KVMGuestRebuildRootTask) OnRebuildRootDiskCompleteFailed(ctx context.Context, guest *models.SGuest, data jsonutils.JSONObject) { +func (self *KVMGuestRebuildRootTask) OnRebuildRootDiskCompleteFailed(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) { + guest := obj.(*models.SGuest) + self.SetStageFailed(ctx, data.String()) logclient.AddActionLog(guest, logclient.ACT_VM_REBUILD, data, self.UserCred, false) } -func (self *KVMGuestRebuildRootTask) OnGuestDeployComplete(ctx context.Context, guest *models.SGuest, data jsonutils.JSONObject) { +func (self *KVMGuestRebuildRootTask) OnGuestDeployComplete(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) { + guest := obj.(*models.SGuest) + self.SetStageComplete(ctx, nil) logclient.AddActionLog(guest, logclient.ACT_VM_REBUILD, nil, self.UserCred, true) } + + +type ManagedGuestRebuildRootTask struct { + SGuestBaseTask +} + +func (self *ManagedGuestRebuildRootTask) OnInit(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) { + guest := obj.(*models.SGuest) + + diskCat := guest.CategorizeDisks() + imageId := diskCat.Root.GetTemplateId() + storage := diskCat.Root.GetStorage() + cache := storage.GetStoragecache() + + self.SetStage("OnHostCacheImageComplete", nil) + cache.StartImageCacheTask(ctx, self.UserCred, imageId, false, self.GetTaskId()) +} + +func (self *ManagedGuestRebuildRootTask) OnHostCacheImageComplete(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) { + guest := obj.(*models.SGuest) + + self.SetStage("OnGuestDeployComplete", nil) + guest.SetStatus(self.UserCred, models.VM_DEPLOYING, "rebuild deploy") + guest.StartGuestDeployTask(ctx, self.UserCred, self.Params, "rebuild", self.GetTaskId()) +} + +func (self *ManagedGuestRebuildRootTask) OnHostCacheImageCompleteFailed(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) { + guest := obj.(*models.SGuest) + + self.SetStageFailed(ctx, data.String()) + logclient.AddActionLog(guest, logclient.ACT_VM_REBUILD, data, self.UserCred, false) +} + +func (self *ManagedGuestRebuildRootTask) OnGuestDeployComplete(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) { + self.SetStageComplete(ctx, nil) +} \ No newline at end of file diff --git a/pkg/util/aliyun/host.go b/pkg/util/aliyun/host.go index 24c5a2a1b1..bc586d7427 100644 --- a/pkg/util/aliyun/host.go +++ b/pkg/util/aliyun/host.go @@ -2,13 +2,6 @@ package aliyun import ( "fmt" - "strconv" - "strings" - "time" - - "github.com/aokoli/goutils" - "golang.org/x/crypto/ssh" - "yunion.io/x/jsonutils" "yunion.io/x/log" "yunion.io/x/onecloud/pkg/cloudprovider" @@ -170,8 +163,10 @@ func (self *SHost) GetInstanceById(instanceId string) (*SInstance, error) { return inst, nil } -func (self *SHost) CreateVM(name string, imgId string, sysDiskSize int, cpu int, memMB int, vswitchId string, ipAddr string, desc string, passwd string, storageType string, diskSizes []int, publicKey string) (cloudprovider.ICloudVM, error) { - vmId, err := self._createVM(name, imgId, sysDiskSize, cpu, memMB, vswitchId, ipAddr, desc, passwd, storageType, diskSizes, publicKey) +func (self *SHost) CreateVM(name string, imgId string, sysDiskSize int, cpu int, memMB int, + vswitchId string, ipAddr string, desc string, passwd string, + storageType string, diskSizes []int, publicKey string, secgroupId string) (cloudprovider.ICloudVM, error) { + vmId, err := self._createVM(name, imgId, sysDiskSize, cpu, memMB, vswitchId, ipAddr, desc, passwd, storageType, diskSizes, publicKey, secgroupId) if err != nil { return nil, err } @@ -183,36 +178,9 @@ func (self *SHost) CreateVM(name string, imgId string, sysDiskSize int, cpu int, return vm, err } -func (self *SHost) lookUpAliyunKeypair(publicKey string) (string, error) { - pk, _, _, _, err := ssh.ParseAuthorizedKey([]byte(publicKey)) - if err != nil { - return "", fmt.Errorf("publicKey error %s", err) - } - - fingerprint := strings.Replace(ssh.FingerprintLegacyMD5(pk), ":", "", -1) - ks, total, err := self.zone.region.GetKeypairs(fingerprint, "*", 0, 1) - if total < 1 { - return "", fmt.Errorf("keypair not found %s", err) - } else { - return ks[0].KeyPairName, nil - } -} - -func (self *SHost) importAliyunKeypair(publicKey string) (string, error) { - prefix, e := goutils.RandomAlphabetic(6) - if e != nil { - return "", fmt.Errorf("publicKey error %s", e) - } - - name := prefix + strconv.FormatInt(time.Now().Unix(), 10) - if k, e := self.zone.region.ImportKeypair(name, publicKey); e != nil { - return "", fmt.Errorf("keypair import error %s", e) - } else { - return k.KeyPairName, nil - } -} - -func (self *SHost) _createVM(name string, imgId string, sysDiskSize int, cpu int, memMB int, vswitchId string, ipAddr string, desc string, passwd string, storageType string, diskSizes []int, publicKey string) (string, error) { +func (self *SHost) _createVM(name string, imgId string, sysDiskSize int, cpu int, memMB int, + vswitchId string, ipAddr string, desc string, passwd string, + storageType string, diskSizes []int, publicKey string, secgroupId string) (string, error) { net := self.zone.getNetworkById(vswitchId) if net == nil { return "", fmt.Errorf("invalid switch ID %s", vswitchId) @@ -226,33 +194,31 @@ func (self *SHost) _createVM(name string, imgId string, sysDiskSize int, cpu int return "", fmt.Errorf("vsiwtch's wire's vpc is empty") } - secgroups, err := net.wire.vpc.GetISecurityGroups() - if err != nil { - return "", fmt.Errorf("get security group error %s", err) - } + var err error - var secgroupId string - if len(secgroups) == 0 { - secId, err := self.zone.region.createDefaultSecurityGroup(net.wire.vpc.VpcId) + if len(secgroupId) == 0 { + secgroups, err := net.wire.vpc.GetISecurityGroups() if err != nil { - return "", fmt.Errorf("no secgroup for vpc and failed to create a default One!!") + return "", fmt.Errorf("get security group error %s", err) + } + + if len(secgroups) == 0 { + secId, err := self.zone.region.createDefaultSecurityGroup(net.wire.vpc.VpcId) + if err != nil { + return "", fmt.Errorf("no secgroup for vpc and failed to create a default One!!") + } else { + secgroupId = secId + } } else { - secgroupId = secId + secgroupId = secgroups[0].GetId() } - } else { - secgroupId = secgroups[0].GetId() } keypair := "" if len(publicKey) > 0 { - if name, e := self.lookUpAliyunKeypair(publicKey); e != nil { - if newName, err := self.importAliyunKeypair(publicKey); e != nil { - keypair = newName - } else { - return "", err - } - } else { - keypair = name + keypair, err = self.zone.region.syncKeypair(publicKey) + if err != nil { + return "", err } } diff --git a/pkg/util/aliyun/image.go b/pkg/util/aliyun/image.go index 9de2676629..2955cb6bfe 100644 --- a/pkg/util/aliyun/image.go +++ b/pkg/util/aliyun/image.go @@ -227,7 +227,7 @@ func (self *SRegion) GetImages(status ImageStatusType, owner ImageOwnerType, ima params["ImageName"] = name } - log.Debugf("%s", params) + // log.Debugf("%s", params) body, err := self.ecsRequest("DescribeImages", params) if err != nil { diff --git a/pkg/util/aliyun/instance.go b/pkg/util/aliyun/instance.go index 934889e7d5..8ff283bac8 100644 --- a/pkg/util/aliyun/instance.go +++ b/pkg/util/aliyun/instance.go @@ -4,8 +4,6 @@ import ( "fmt" "time" - "yunion.io/x/onecloud/pkg/util/seclib2" - "yunion.io/x/jsonutils" "yunion.io/x/log" "yunion.io/x/pkg/util/osprofile" @@ -355,25 +353,35 @@ func (self *SInstance) UpdateVM(name string) error { return self.host.zone.region.UpdateVM(self.InstanceId, name) } -func (self *SInstance) DeployVM(name string, password string, publicKey string, resetPassword bool, deleteKeypair bool, description string) error { +func (self *SInstance) DeployVM(name string, password string, publicKey string, deleteKeypair bool, description string) error { var keypairName string if len(publicKey) > 0 { - key, e := self.host.lookUpAliyunKeypair(publicKey) - if e != nil { - key, e = self.host.importAliyunKeypair(publicKey) - if e != nil { - return e - } + var err error + keypairName, err = self.host.zone.region.syncKeypair(publicKey) + if err != nil { + return err } - - keypairName = key } - return self.host.zone.region.DeployVM(self.InstanceId, name, password, keypairName, resetPassword, deleteKeypair, description) + return self.host.zone.region.DeployVM(self.InstanceId, name, password, keypairName, deleteKeypair, description) } -func (self *SInstance) RebuildRoot(imageId string) error { - return self.host.zone.region.ReplaceSystemDisk(self.InstanceId, imageId) +func (self *SInstance) RebuildRoot(imageId string, passwd string, publicKey string, sysSizeGB int) (string, error) { + keypair := "" + if len(publicKey) > 0 { + var err error + keypair, err = self.host.zone.region.syncKeypair(publicKey) + if err != nil { + return "", err + } + } + diskId, err := self.host.zone.region.ReplaceSystemDisk(self.InstanceId, imageId, passwd, keypair, sysSizeGB) + if err != nil { + return "", err + } + self.idisks = nil + + return diskId, nil } func (self *SInstance) ChangeConfig(instanceId string, ncpu int, vmem int) error { @@ -551,7 +559,7 @@ func (self *SRegion) DeleteVM(instanceId string) error { // } } -func (self *SRegion) DeployVM(instanceId string, name string, password string, keypairName string, resetPassword bool, deleteKeypair bool, description string) error { +func (self *SRegion) DeployVM(instanceId string, name string, password string, keypairName string, deleteKeypair bool, description string) error { instance, err := self.GetInstance(instanceId) if err != nil { return err @@ -559,17 +567,24 @@ func (self *SRegion) DeployVM(instanceId string, name string, password string, k // 修改密钥时直接返回 if deleteKeypair { - return self.DetachKeyPair(instanceId, instance.KeyPairName) + err = self.DetachKeyPair(instanceId, instance.KeyPairName) + if err != nil { + return err + } } if len(keypairName) > 0 { - return self.AttachKeypair(instanceId, keypairName) + err = self.AttachKeypair(instanceId, keypairName) + if err != nil { + return err + } } params := make(map[string]string) - if resetPassword { - params["Password"] = seclib2.RandomPassword2(12) - } + + // if resetPassword { + // params["Password"] = seclib2.RandomPassword2(12) + // } // 指定密码的情况下,使用指定的密码 if len(password) > 0 { params["Password"] = password @@ -622,10 +637,28 @@ func (self *SRegion) modifyInstanceAttribute(instanceId string, params map[strin return self.instanceOperation(instanceId, "ModifyInstanceAttribute", params) } -func (self *SRegion) ReplaceSystemDisk(instanceId string, image string) error { +func (self *SRegion) ReplaceSystemDisk(instanceId string, imageId string, passwd string, keypairName string, sysDiskSizeGB int) (string, error) { params := make(map[string]string) - params["ImageId"] = image - return self.instanceOperation(instanceId, "ReplaceSystemDisk", params) + params["RegionId"] = self.RegionId + params["InstanceId"] = instanceId + params["ImageId"] = imageId + if len(passwd) > 0 { + params["Password"] = passwd + } else { + params["PasswordInherit"] = "True" + } + if len(keypairName) > 0 { + params["KeyPairName"] = keypairName + } + if sysDiskSizeGB > 0 { + params["SystemDisk.Size"] = fmt.Sprintf("%d", sysDiskSizeGB) + } + body, err := self.ecsRequest("ReplaceSystemDisk", params) + if err != nil { + return "", err + } + // log.Debugf("%s", body.String()) + return body.GetString("DiskId") } func (self *SRegion) ChangeVMConfig(zoneId string, instanceId string, ncpu int, vmem int, disks []*SDisk) error { @@ -685,7 +718,7 @@ func (self *SInstance) SyncSecurityGroup(secgroupId string, name string, rules [ return err } } - } else if secgrpId, err := vpc.syncSecurityGroup(secgroupId, name, rules); err != nil { + } else if secgrpId, err := vpc.SyncSecurityGroup(secgroupId, name, rules); err != nil { return err } else if err := vpc.assignSecurityGroup(secgrpId, self.InstanceId); err != nil { return err diff --git a/pkg/util/aliyun/keypair.go b/pkg/util/aliyun/keypair.go index e3b2a504a8..1259e8c544 100644 --- a/pkg/util/aliyun/keypair.go +++ b/pkg/util/aliyun/keypair.go @@ -4,6 +4,11 @@ import ( "encoding/json" "fmt" "yunion.io/x/log" + "golang.org/x/crypto/ssh" + "strings" + "github.com/aokoli/goutils" + "strconv" + "time" ) type SKeypair struct { @@ -93,3 +98,40 @@ func (self *SRegion) DetachKeyPair(instanceId string, name string) error { return nil } + +func (self *SRegion) lookUpAliyunKeypair(publicKey string) (string, error) { + pk, _, _, _, err := ssh.ParseAuthorizedKey([]byte(publicKey)) + if err != nil { + return "", fmt.Errorf("publicKey error %s", err) + } + + fingerprint := strings.Replace(ssh.FingerprintLegacyMD5(pk), ":", "", -1) + ks, total, err := self.GetKeypairs(fingerprint, "*", 0, 1) + if total < 1 { + return "", fmt.Errorf("keypair not found %s", err) + } else { + return ks[0].KeyPairName, nil + } +} + +func (self *SRegion) importAliyunKeypair(publicKey string) (string, error) { + prefix, e := goutils.RandomAlphabetic(6) + if e != nil { + return "", fmt.Errorf("publicKey error %s", e) + } + + name := prefix + strconv.FormatInt(time.Now().Unix(), 10) + if k, e := self.ImportKeypair(name, publicKey); e != nil { + return "", fmt.Errorf("keypair import error %s", e) + } else { + return k.KeyPairName, nil + } +} + +func (self *SRegion) syncKeypair(publicKey string) (string, error) { + name, e := self.lookUpAliyunKeypair(publicKey) + if e == nil { + return name, nil + } + return self.importAliyunKeypair(publicKey) +} \ No newline at end of file diff --git a/pkg/util/aliyun/region.go b/pkg/util/aliyun/region.go index 4fc9d104b9..8dfa8f89db 100644 --- a/pkg/util/aliyun/region.go +++ b/pkg/util/aliyun/region.go @@ -422,7 +422,7 @@ func (self *SRegion) CreateInstanceSimple(name string, imgId string, cpu int, me log.Debugf("Search in zone %s", z.LocalName) net := z.getNetworkById(vswitchId) if net != nil { - inst, err := z.getHost().CreateVM(name, imgId, 0, cpu, memGB*1024, vswitchId, "", "", passwd, storageType, dataDiskSizesGB, publicKey) + inst, err := z.getHost().CreateVM(name, imgId, 0, cpu, memGB*1024, vswitchId, "", "", passwd, storageType, dataDiskSizesGB, publicKey, "") if err != nil { return nil, err } diff --git a/pkg/util/aliyun/shell/instance.go b/pkg/util/aliyun/shell/instance.go index 28b1a73d36..45b5f740e3 100644 --- a/pkg/util/aliyun/shell/instance.go +++ b/pkg/util/aliyun/shell/instance.go @@ -114,12 +114,12 @@ func init() { Keypair string `help:"Keypair Name"` DeleteKeypair bool `help:"Remove SSH keypair"` Password string `help:"new password"` - ResetPassword bool `help:"Force reset password"` + // ResetPassword bool `help:"Force reset password"` Description string `help:"new instances description"` } shellutils.R(&InstanceDeployOptions{}, "instance-deploy", "Deploy keypair/password to a stopped virtual server", func(cli *aliyun.SRegion, args *InstanceDeployOptions) error { - err := cli.DeployVM(args.ID, args.Name, args.Password, args.Keypair, args.ResetPassword, args.DeleteKeypair, args.Description) + err := cli.DeployVM(args.ID, args.Name, args.Password, args.Keypair, args.DeleteKeypair, args.Description) if err != nil { return err } @@ -129,13 +129,17 @@ func init() { type InstanceRebuildRootOptions struct { ID string `help:"instance ID"` Image string `help:"Image ID"` + Password string `help:"pasword"` + Keypair string `help:"keypair name"` + Size int `help:"system disk size in GB"` } shellutils.R(&InstanceRebuildRootOptions{}, "instance-rebuild-root", "Reinstall virtual server system image", func(cli *aliyun.SRegion, args *InstanceRebuildRootOptions) error { - err := cli.ReplaceSystemDisk(args.ID, args.Image) + diskID, err := cli.ReplaceSystemDisk(args.ID, args.Image, args.Password, args.Keypair, args.Size) if err != nil { return err } + fmt.Printf("New diskID is %s", diskID) return nil }) diff --git a/pkg/util/aliyun/vpc.go b/pkg/util/aliyun/vpc.go index ea645ff2be..a7e64fb2fe 100644 --- a/pkg/util/aliyun/vpc.go +++ b/pkg/util/aliyun/vpc.go @@ -204,7 +204,7 @@ func (self *SVpc) Delete() error { return self.region.DeleteVpc(self.VpcId) } -func (self *SVpc) syncSecurityGroup(secgroupId string, name string, rules []secrules.SecurityRule) (string, error) { +func (self *SVpc) SyncSecurityGroup(secgroupId string, name string, rules []secrules.SecurityRule) (string, error) { secgrpId := "" if secgroup, err := self.region.getSecurityGroupByTag(self.VpcId, secgroupId); err != nil { if secgrpId, err = self.region.createSecurityGroup(self.VpcId, name, ""); err != nil { @@ -232,10 +232,11 @@ func (self *SVpc) syncSecurityGroup(secgroupId string, name string, rules []secr } return secgrpId, nil } + func (self *SVpc) assignSecurityGroup(secgroupId string, instanceId string) error { return self.region.assignSecurityGroup(secgroupId, instanceId) } func (self *SVpc) revokeSecurityGroup(secgroupId string, instanceId string, keep bool) error { return self.region.revokeSecurityGroup(secgroupId, instanceId, keep) -} +} \ No newline at end of file diff --git a/pkg/util/esxi/host.go b/pkg/util/esxi/host.go index 3a9a053c5d..8b03a3814f 100644 --- a/pkg/util/esxi/host.go +++ b/pkg/util/esxi/host.go @@ -358,7 +358,7 @@ func (self *SHost) GetManagerId() string { } func (self *SHost) CreateVM(name string, imgId string, sysDiskSize int, cpu int, memMB int, vswitchId string, ipAddr string, desc string, - passwd string, storageType string, diskSizes []int, publicKey string) (cloudprovider.ICloudVM, error) { + passwd string, storageType string, diskSizes []int, publicKey string, secGrpId string) (cloudprovider.ICloudVM, error) { log.Debugf("CreateVM") return nil, cloudprovider.ErrNotImplemented } diff --git a/pkg/util/esxi/virtualmachine.go b/pkg/util/esxi/virtualmachine.go index 1ba9b35965..8996884109 100644 --- a/pkg/util/esxi/virtualmachine.go +++ b/pkg/util/esxi/virtualmachine.go @@ -69,12 +69,12 @@ func (self *SVirtualMachine) IsEmulated() bool { return false } -func (self *SVirtualMachine) DeployVM(name string, password string, publicKey string, resetPassword bool, deleteKeypair bool, description string) error { +func (self *SVirtualMachine) DeployVM(name string, password string, publicKey string, deleteKeypair bool, description string) error { return cloudprovider.ErrNotImplemented } -func (self *SVirtualMachine) RebuildRoot(imageId string) error { - return cloudprovider.ErrNotImplemented +func (self *SVirtualMachine) RebuildRoot(imageId string, passwd string, publicKey string, sysSizeGB int) (string, error) { + return "", cloudprovider.ErrNotImplemented } func (self *SVirtualMachine) UpdateVM(name string) error { diff --git a/pkg/util/logclient/logclient.go b/pkg/util/logclient/logclient.go index 82f3e76fd0..1d73ccc3f2 100644 --- a/pkg/util/logclient/logclient.go +++ b/pkg/util/logclient/logclient.go @@ -1,8 +1,10 @@ package logclient import ( - "fmt" + "yunion.io/x/log" "yunion.io/x/jsonutils" + + "yunion.io/x/onecloud/pkg/appsrv" "yunion.io/x/onecloud/pkg/mcclient" "yunion.io/x/onecloud/pkg/mcclient/auth" "yunion.io/x/onecloud/pkg/mcclient/modules" @@ -53,6 +55,12 @@ const ( ACT_VM_UNBIND_KEYPAIR = "解绑密钥" ) +var logclientWorkerMan *appsrv.WorkerManager + +func init() { + logclientWorkerMan = appsrv.NewWorkerManager("LogClientWorkerManager", 1, 50) +} + type IObject interface { GetId() string GetName() string @@ -64,8 +72,6 @@ func AddActionLog(model IObject, action string, iNotes interface{}, userCred mcc token := userCred notes := stringutils.Interface2String(iNotes) - s := auth.GetSession(userCred, "", "") - logentry := jsonutils.NewDict() logentry.Add(jsonutils.NewString(model.GetName()), "obj_name") logentry.Add(jsonutils.NewString(model.Keyword()), "obj_type") @@ -86,10 +92,11 @@ func AddActionLog(model IObject, action string, iNotes interface{}, userCred mcc logentry.Add(jsonutils.NewString(notes), "notes") - _, err := modules.Actions.Create(s, logentry) - if err != nil { - fmt.Printf("create action log failed %s", err) - } else { - fmt.Println("create action log success") - } + logclientWorkerMan.Run(func() { + s := auth.GetSession(userCred, "", "") + _, err := modules.Actions.Create(s, logentry) + if err != nil { + log.Errorf("create action log failed %s", err) + } + }, nil) } From 447b0b4a1cef1559d8ab4d1ee02508b96218a444 Mon Sep 17 00:00:00 2001 From: Qiu Jian Date: Fri, 14 Sep 2018 01:42:59 +0800 Subject: [PATCH 3/9] undo changes to virtualjointbase.go --- pkg/cloudcommon/db/virtualjointbase.go | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/pkg/cloudcommon/db/virtualjointbase.go b/pkg/cloudcommon/db/virtualjointbase.go index cec26d938f..a4f5a4a853 100644 --- a/pkg/cloudcommon/db/virtualjointbase.go +++ b/pkg/cloudcommon/db/virtualjointbase.go @@ -60,22 +60,6 @@ func (manager *SVirtualJointResourceBaseManager) AllowAttach(ctx context.Context return false } -/*func (joint *SVirtualJointResourceBase) GetJointModelManager() IJointModelManager { - -}*/ - -func (joint *SVirtualJointResourceBase) Detach(ctx context.Context, userCred mcclient.TokenCredential) error { - return nil -} - -func (joint *SVirtualJointResourceBase) Master() IStandaloneModel { - return JointMaster(joint) -} - -func (joint *SVirtualJointResourceBase) Slave() IStandaloneModel { - return JointSlave(joint) -} - func (self *SVirtualJointResourceBase) AllowGetDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool { masterVirtual := self.Master().(IVirtualModel) return masterVirtual.IsOwner(userCred) From 314d491e442e88e9fc758d612121878671b011a4 Mon Sep 17 00:00:00 2001 From: Qiu Jian Date: Fri, 14 Sep 2018 09:32:42 +0800 Subject: [PATCH 4/9] Update vendor --- Gopkg.lock | 12 ++-- vendor/yunion.io/x/jsonutils/unmarshal.go | 7 ++- vendor/yunion.io/x/pkg/util/fifoutils/doc.go | 1 - .../x/pkg/util/fifoutils/fifoutils.go | 58 ------------------- vendor/yunion.io/x/sqlchemy/query.go | 1 - 5 files changed, 10 insertions(+), 69 deletions(-) delete mode 100644 vendor/yunion.io/x/pkg/util/fifoutils/doc.go delete mode 100644 vendor/yunion.io/x/pkg/util/fifoutils/fifoutils.go diff --git a/Gopkg.lock b/Gopkg.lock index 8c2d15daa6..35c2526f1c 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -1105,11 +1105,11 @@ [[projects]] branch = "master" - digest = "1:d0257638bb52243f9fa293ef07544081259d54edd8e53f084e522b6c22e92637" + digest = "1:b71bcbb9d425fa8d1d0f1932e275768930808600a24368abd62b5fc4a945c32b" name = "yunion.io/x/jsonutils" packages = ["."] pruneopts = "UT" - revision = "38477c9cceb895816fe21507d73da30376d358b7" + revision = "6dc5b0d8959336346efaacd9c9ab1defa80d5ee9" [[projects]] branch = "master" @@ -1124,7 +1124,7 @@ [[projects]] branch = "master" - digest = "1:47660d9bad5ac13797da13236922903a26206c81aa2df28af44b3e7ddd3e3f06" + digest = "1:253553e4aea4744856900c4015036177f56ddfa4400a4422a2aab8cb1737bd00" name = "yunion.io/x/pkg" packages = [ "gotypes", @@ -1135,7 +1135,6 @@ "util/clock", "util/compare", "util/errors", - "util/fifoutils", "util/fileutils", "util/filterclause", "util/goroutine_pool", @@ -1163,11 +1162,11 @@ [[projects]] branch = "master" - digest = "1:a8a402e8c9d4a7bf8efbdb81ae2da8fb72ebbe07bbfc1168a124905ace456ed0" + digest = "1:e52c4c426ba5bbb92b2d6b2f20f694ff836dde16151adaca39c86d5fb010fea9" name = "yunion.io/x/sqlchemy" packages = ["."] pruneopts = "UT" - revision = "1e2883d085d6d449d4df7d28b61073dec11c5dbb" + revision = "946ed13318f85073e55dbb8105593c710e943a31" [[projects]] branch = "master" @@ -1253,7 +1252,6 @@ "yunion.io/x/pkg/util/cache", "yunion.io/x/pkg/util/compare", "yunion.io/x/pkg/util/errors", - "yunion.io/x/pkg/util/fifoutils", "yunion.io/x/pkg/util/fileutils", "yunion.io/x/pkg/util/filterclause", "yunion.io/x/pkg/util/goroutine_pool", diff --git a/vendor/yunion.io/x/jsonutils/unmarshal.go b/vendor/yunion.io/x/jsonutils/unmarshal.go index 941fcdf729..6938c1ee09 100644 --- a/vendor/yunion.io/x/jsonutils/unmarshal.go +++ b/vendor/yunion.io/x/jsonutils/unmarshal.go @@ -87,9 +87,12 @@ func (this *JSONInt) unmarshalValue(val reflect.Value) error { } } switch val.Kind() { - case reflect.Int, reflect.Uint, reflect.Int8, reflect.Uint8, - reflect.Int16, reflect.Uint16, reflect.Int32, reflect.Uint32, reflect.Int64, reflect.Uint64: + case reflect.Int, reflect.Int8, reflect.Int16, + reflect.Int32, reflect.Int64: val.SetInt(this.data) + case reflect.Uint, reflect.Uint8, reflect.Uint16, + reflect.Uint32, reflect.Uint64: + val.SetUint(uint64(this.data)) case reflect.Float32, reflect.Float64: val.SetFloat(float64(this.data)) case reflect.Bool: diff --git a/vendor/yunion.io/x/pkg/util/fifoutils/doc.go b/vendor/yunion.io/x/pkg/util/fifoutils/doc.go deleted file mode 100644 index d217b0e855..0000000000 --- a/vendor/yunion.io/x/pkg/util/fifoutils/doc.go +++ /dev/null @@ -1 +0,0 @@ -package fifoutils // import "yunion.io/x/pkg/util/fifoutils" diff --git a/vendor/yunion.io/x/pkg/util/fifoutils/fifoutils.go b/vendor/yunion.io/x/pkg/util/fifoutils/fifoutils.go deleted file mode 100644 index f96b05583e..0000000000 --- a/vendor/yunion.io/x/pkg/util/fifoutils/fifoutils.go +++ /dev/null @@ -1,58 +0,0 @@ -package fifoutils - -import ( - "errors" - - "yunion.io/x/log" -) - -var ErrEmpty error - -func init() { - ErrEmpty = errors.New("fifo is empty") -} - -type FIFO struct { - array []interface{} - len int -} - -func NewFIFO() *FIFO { - fifo := FIFO{array: make([]interface{}, 0), len: 0} - return &fifo -} - -func (f *FIFO) Push(ele interface{}) { - if f.len < len(f.array) { - f.array[f.len] = ele - } else { - f.array = append(f.array, ele) - } - f.len += 1 -} - -func (f *FIFO) Pop() interface{} { - if f.len <= 0 { - return nil - } - ele := f.array[0] - f.len -= 1 - for i := 0; i < f.len; i += 1 { - f.array[i] = f.array[i+1] - } - f.array[f.len] = nil - return ele -} - -func (f *FIFO) Len() int { - return f.len -} - -func (f *FIFO) ElementAt(idx int) interface{} { - if idx >= 0 && idx < f.len { - return f.array[idx] - } else { - log.Fatalf("Out of index") - return nil - } -} diff --git a/vendor/yunion.io/x/sqlchemy/query.go b/vendor/yunion.io/x/sqlchemy/query.go index 619240006e..d3ae2dfeca 100644 --- a/vendor/yunion.io/x/sqlchemy/query.go +++ b/vendor/yunion.io/x/sqlchemy/query.go @@ -501,7 +501,6 @@ func (q *SQuery) First(dest interface{}) error { func (q *SQuery) All(dest interface{}) error { arrayType := reflect.TypeOf(dest).Elem() - fmt.Println(arrayType, arrayType.Kind()) if arrayType.Kind() != reflect.Array && arrayType.Kind() != reflect.Slice { return fmt.Errorf("dest is not an array or slice") From 3f8604e54902218634318a8ac76b9c8ea89683b9 Mon Sep 17 00:00:00 2001 From: Zexi Li Date: Fri, 14 Sep 2018 11:01:55 +0800 Subject: [PATCH 5/9] climc: k8s-deployment-create support labels --- pkg/mcclient/options/k8s/deployment.go | 36 +++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/pkg/mcclient/options/k8s/deployment.go b/pkg/mcclient/options/k8s/deployment.go index 7d69632601..d68813f4bc 100644 --- a/pkg/mcclient/options/k8s/deployment.go +++ b/pkg/mcclient/options/k8s/deployment.go @@ -17,7 +17,7 @@ type DeploymentCreateOptions struct { Image string `help:"The image for the container to run"` Replicas int64 `help:"Number of replicas for pods in this deployment"` RunAsPrivileged bool `help:"Whether to run the container as privileged user"` - Labels string `help:"Comma separated labels to apply to the pod(s)"` + Labels string `help:"Comma separated labels to apply to the pod(s), e.g. --labels='app=hazelcast,env=prod'"` Env []string `help:"Environment variables to set in container"` Port []string `help:"Port for the service that is created, format is :: e.g. tcp:80:3000"` Net string `help:"Network config, e.g. net1, net1:10.168.222.171"` @@ -62,6 +62,13 @@ func (o DeploymentCreateOptions) Params() (*jsonutils.JSONDict, error) { } params.Add(net, "networkConfig") } + if o.Labels != "" { + labels, err := parseLabels(o.Labels) + if err != nil { + return nil, err + } + params.Add(labels, "labels") + } return params, nil } @@ -145,3 +152,30 @@ func (o DeploymentCreateFromFileOptions) Params() (*jsonutils.JSONDict, error) { params.Add(jsonutils.NewString(string(content)), "content") return params, nil } + +func parseLabels(labelStr string) (*jsonutils.JSONArray, error) { + labelsStrs := strings.Split(labelStr, ",") + ret := jsonutils.NewArray() + if len(labelsStrs) == 0 { + return ret, nil + } + for _, str := range labelsStrs { + label, err := parseLabel(str) + if err != nil { + return nil, err + } + ret.Add(label) + } + return ret, nil +} + +func parseLabel(str string) (jsonutils.JSONObject, error) { + parts := strings.Split(str, "=") + if len(parts) != 2 { + return nil, fmt.Errorf("Invalid label string: %s", str) + } + label := jsonutils.NewDict() + label.Add(jsonutils.NewString(parts[0]), "key") + label.Add(jsonutils.NewString(parts[1]), "value") + return label, nil +} From 1c427f3c1b4dbac1098f74139cc09d4007b840ef Mon Sep 17 00:00:00 2001 From: Qiu Jian Date: Fri, 14 Sep 2018 11:18:20 +0800 Subject: [PATCH 6/9] =?UTF-8?q?=E4=BF=AE=E6=AD=A3rebuildroot=E5=90=8EAPI?= =?UTF-8?q?=E8=8E=B7=E5=8F=96=E7=9A=84=E7=A3=81=E7=9B=98=E5=88=97=E8=A1=A8?= =?UTF-8?q?=E4=B8=8D=E5=AE=8C=E6=95=B4=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pkg/compute/guestdrivers/aliyun.go | 33 ++++++++++++++++++++++++++++-- pkg/util/aliyun/instance.go | 25 +++++++--------------- 2 files changed, 38 insertions(+), 20 deletions(-) diff --git a/pkg/compute/guestdrivers/aliyun.go b/pkg/compute/guestdrivers/aliyun.go index 65dadac6ea..908bad6ff4 100644 --- a/pkg/compute/guestdrivers/aliyun.go +++ b/pkg/compute/guestdrivers/aliyun.go @@ -379,18 +379,47 @@ func (self *SAliyunGuestDriver) RequestDeployGuestOnHost(ctx context.Context, gu } taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) { - _, err := iVM.RebuildRoot(desc.ExternalImageId, passwd, publicKey, desc.SysDiskSize) + diskId, err := iVM.RebuildRoot(desc.ExternalImageId, passwd, publicKey, desc.SysDiskSize) if err != nil { return nil, err } - log.Debugf("VMrebuildRoot %s, wait status ready ...", iVM.GetGlobalId()) + log.Debugf("VMrebuildRoot %s new diskID %s, wait status ready ...", iVM.GetGlobalId(), diskId) + err = cloudprovider.WaitStatus(iVM, models.VM_READY, time.Second*5, time.Second*1800) if err != nil { return nil, err } log.Debugf("VMrebuildRoot %s, and status is ready", iVM.GetGlobalId()) + maxWaitSecs := 300 + waited := 0 + + for { + // hack, wait disk number consistent + idisks, err := iVM.GetIDisks() + if err != nil { + log.Errorf("fail to find VM idisks %s", err) + return nil, err + } + if len(idisks) < len(desc.DataDisks) + 1 { + if waited > maxWaitSecs { + log.Errorf("inconsistent disk number, wait timeout, must be something wrong one remote") + return nil, cloudprovider.ErrTimeout + } + log.Debugf("inconsistent disk number???? %d != %d", len(idisks), len(desc.DataDisks)+1) + time.Sleep(time.Second*5) + waited += 5 + } else { + if idisks[0].GetGlobalId() != diskId { + log.Errorf("system disk id inconsistent %s != %s", idisks[0].GetGlobalId(), diskId) + return nil, fmt.Errorf("inconsistent sys disk id after rebuild root") + } + + break + } + } + data := fetchIVMinfo(desc, iVM, guest.Id, passwd) return data, nil diff --git a/pkg/util/aliyun/instance.go b/pkg/util/aliyun/instance.go index 8ff283bac8..64afa92839 100644 --- a/pkg/util/aliyun/instance.go +++ b/pkg/util/aliyun/instance.go @@ -66,7 +66,7 @@ type SVpcAttributes struct { type SInstance struct { host *SHost - idisks []cloudprovider.ICloudDisk + // idisks []cloudprovider.ICloudDisk AutoReleaseTime string ClusterId string @@ -187,35 +187,25 @@ func (self *SInstance) getVpc() (*SVpc, error) { return self.host.zone.region.getVpc(self.VpcAttributes.VpcId) } -func (self *SInstance) fetchDisks() error { +func (self *SInstance) GetIDisks() ([]cloudprovider.ICloudDisk, error) { disks, total, err := self.host.zone.region.GetDisks(self.InstanceId, "", "", nil, 0, 50) if err != nil { log.Errorf("fetchDisks fail %s", err) - return err + return nil, err } if total > len(disks) { disks, _, err = self.host.zone.region.GetDisks(self.InstanceId, "", "", nil, 0, total) } - self.idisks = make([]cloudprovider.ICloudDisk, len(disks)) + idisks := make([]cloudprovider.ICloudDisk, len(disks)) for i := 0; i < len(disks); i += 1 { store, err := self.host.zone.getStorageByCategory(disks[i].Category) - if err != nil { - return err - } - disks[i].storage = store - self.idisks[i] = &disks[i] - } - return nil -} - -func (self *SInstance) GetIDisks() ([]cloudprovider.ICloudDisk, error) { - if self.idisks == nil { - err := self.fetchDisks() if err != nil { return nil, err } + disks[i].storage = store + idisks[i] = &disks[i] } - return self.idisks, nil + return idisks, nil } func (self *SInstance) GetINics() ([]cloudprovider.ICloudNic, error) { @@ -379,7 +369,6 @@ func (self *SInstance) RebuildRoot(imageId string, passwd string, publicKey stri if err != nil { return "", err } - self.idisks = nil return diskId, nil } From 9af8de9bf8666ef0db1fcb950f534028f36b46e2 Mon Sep 17 00:00:00 2001 From: Qiu Jian Date: Fri, 14 Sep 2018 11:57:17 +0800 Subject: [PATCH 7/9] minior fixes --- pkg/compute/guestdrivers/aliyun.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/compute/guestdrivers/aliyun.go b/pkg/compute/guestdrivers/aliyun.go index 4f15cc0cf6..aeef133b15 100644 --- a/pkg/compute/guestdrivers/aliyun.go +++ b/pkg/compute/guestdrivers/aliyun.go @@ -329,7 +329,7 @@ func (self *SAliyunGuestDriver) RequestDeployGuestOnHost(ctx context.Context, gu name, _ := params.GetString("name") description, _ := params.GetString("description") publicKey, _ := config.GetString("public_key") - resetPassword := jsonutils.QueryBoolean(params, "reset_password", false) + // resetPassword := jsonutils.QueryBoolean(params, "reset_password", false) deleteKeypair := jsonutils.QueryBoolean(params, "__delete_keypair__", false) //password, _ := params.GetString("password") //if resetPassword && len(password) == 0 { From a70fee75301c9c71cc4aed7db1faa3e42f9cf183 Mon Sep 17 00:00:00 2001 From: Zexi Li Date: Fri, 14 Sep 2018 11:34:09 +0800 Subject: [PATCH 8/9] climc: add name filter to k8s list resource --- pkg/mcclient/options/k8s/base.go | 8 ++++++-- pkg/mcclient/options/k8s/deployment.go | 26 ++++++-------------------- 2 files changed, 12 insertions(+), 22 deletions(-) diff --git a/pkg/mcclient/options/k8s/base.go b/pkg/mcclient/options/k8s/base.go index 384c027251..4a45cc869c 100644 --- a/pkg/mcclient/options/k8s/base.go +++ b/pkg/mcclient/options/k8s/base.go @@ -15,8 +15,9 @@ func (o ClusterBaseOptions) Params() *jsonutils.JSONDict { } type BaseListOptions struct { - Limit int `default:"20" help:"Page limit"` - Offset int `default:"0" help:"page offset"` + Limit int `default:"20" help:"Page limit"` + Offset int `default:"0" help:"Page offset"` + Name string `help:"Search by name"` } func (o BaseListOptions) Params() *jsonutils.JSONDict { @@ -27,6 +28,9 @@ func (o BaseListOptions) Params() *jsonutils.JSONDict { if o.Offset > 0 { params.Add(jsonutils.NewInt(int64(o.Offset)), "offset") } + if o.Name != "" { + params.Add(jsonutils.NewString(o.Name), "name") + } return params } diff --git a/pkg/mcclient/options/k8s/deployment.go b/pkg/mcclient/options/k8s/deployment.go index d68813f4bc..4f9f1fe8ef 100644 --- a/pkg/mcclient/options/k8s/deployment.go +++ b/pkg/mcclient/options/k8s/deployment.go @@ -17,7 +17,7 @@ type DeploymentCreateOptions struct { Image string `help:"The image for the container to run"` Replicas int64 `help:"Number of replicas for pods in this deployment"` RunAsPrivileged bool `help:"Whether to run the container as privileged user"` - Labels string `help:"Comma separated labels to apply to the pod(s), e.g. --labels='app=hazelcast,env=prod'"` + Label []string `help:"Labels to apply to the pod(s), e.g. 'env=prod'"` Env []string `help:"Environment variables to set in container"` Port []string `help:"Port for the service that is created, format is :: e.g. tcp:80:3000"` Net string `help:"Network config, e.g. net1, net1:10.168.222.171"` @@ -62,13 +62,15 @@ func (o DeploymentCreateOptions) Params() (*jsonutils.JSONDict, error) { } params.Add(net, "networkConfig") } - if o.Labels != "" { - labels, err := parseLabels(o.Labels) + labels := jsonutils.NewArray() + for _, label := range o.Label { + label, err := parseLabel(label) if err != nil { return nil, err } - params.Add(labels, "labels") + labels.Add(label) } + params.Add(labels, "labels") return params, nil } @@ -153,22 +155,6 @@ func (o DeploymentCreateFromFileOptions) Params() (*jsonutils.JSONDict, error) { return params, nil } -func parseLabels(labelStr string) (*jsonutils.JSONArray, error) { - labelsStrs := strings.Split(labelStr, ",") - ret := jsonutils.NewArray() - if len(labelsStrs) == 0 { - return ret, nil - } - for _, str := range labelsStrs { - label, err := parseLabel(str) - if err != nil { - return nil, err - } - ret.Add(label) - } - return ret, nil -} - func parseLabel(str string) (jsonutils.JSONObject, error) { parts := strings.Split(str, "=") if len(parts) != 2 { From eb77372794804d7f500c188b28ba24ecda6542b3 Mon Sep 17 00:00:00 2001 From: Zexi Li Date: Fri, 14 Sep 2018 12:25:45 +0800 Subject: [PATCH 9/9] region: network list by cloudregion --- cmd/climc/shell/networks.go | 12 ++++++++---- pkg/compute/models/networks.go | 18 ++++++++++++++++++ 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/cmd/climc/shell/networks.go b/cmd/climc/shell/networks.go index 4fb19e8143..f569dc19d7 100644 --- a/cmd/climc/shell/networks.go +++ b/cmd/climc/shell/networks.go @@ -12,10 +12,11 @@ import ( func init() { type NetworkListOptions struct { options.BaseListOptions - Ip string `help:"search networks that contain this IP"` - Zone string `help:"search networks in a zone"` - Wire string `help:"search networks belongs to a wire"` - Vpc string `help:"search networks belongs to a VPC"` + Ip string `help:"search networks that contain this IP"` + Zone string `help:"search networks in a zone"` + Wire string `help:"search networks belongs to a wire"` + Vpc string `help:"search networks belongs to a VPC"` + Region string `help:"search networks belongs to a CloudRegion"` } R(&NetworkListOptions{}, "network-list", "List networks", func(s *mcclient.ClientSession, args *NetworkListOptions) error { var params *jsonutils.JSONDict @@ -36,6 +37,9 @@ func init() { if len(args.Vpc) > 0 { params.Add(jsonutils.NewString(args.Vpc), "vpc") } + if len(args.Region) > 0 { + params.Add(jsonutils.NewString(args.Region), "cloudregion") + } var result *modules.ListResult var err error if len(args.Wire) > 0 { diff --git a/pkg/compute/models/networks.go b/pkg/compute/models/networks.go index da8954108c..33a529f53a 100644 --- a/pkg/compute/models/networks.go +++ b/pkg/compute/models/networks.go @@ -1211,6 +1211,24 @@ func (manager *SNetworkManager) ListItemFilter(ctx context.Context, q *sqlchemy. sq := WireManager.Query("id").Equals("vpc_id", vpcObj.GetId()) q = q.Filter(sqlchemy.In(q.Field("wire_id"), sq.SubQuery())) } + regionStr := jsonutils.GetAnyString(query, []string{"region_id", "region", "cloudregion_id", "cloudregion"}) + if len(regionStr) > 0 { + region, err := CloudregionManager.FetchByIdOrName(userCred.GetProjectId(), regionStr) + if err != nil { + if err == sql.ErrNoRows { + return nil, httperrors.NewResourceNotFoundError("cloud region %s not found", regionStr) + } else { + return nil, httperrors.NewGeneralError(err) + } + } + wires := WireManager.Query().SubQuery() + vpcs := VpcManager.Query().SubQuery() + sq := wires.Query(wires.Field("id")). + Join(vpcs, sqlchemy.AND( + sqlchemy.Equals(vpcs.Field("cloudregion_id"), region.GetId()), + sqlchemy.Equals(wires.Field("vpc_id"), vpcs.Field("id")))) + q = q.Filter(sqlchemy.In(q.Field("wire_id"), sq.SubQuery())) + } return q, nil }