From 68ed59eda0f14279c2fceb8db56fbae12c86d8c2 Mon Sep 17 00:00:00 2001 From: Yousong Zhou Date: Fri, 14 Jun 2019 09:08:29 +0000 Subject: [PATCH 1/6] =?UTF-8?q?ansibleserver:=20=E5=9C=A8pb=E6=89=A7?= =?UTF-8?q?=E8=A1=8C=E5=A4=B1=E8=B4=A5=E6=97=B6=E6=89=93=E5=87=BA=E8=AD=A6?= =?UTF-8?q?=E5=91=8A=E6=97=A5=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pkg/ansibleserver/models/ansibleplaybooks.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pkg/ansibleserver/models/ansibleplaybooks.go b/pkg/ansibleserver/models/ansibleplaybooks.go index 5a89490491..ed08156557 100644 --- a/pkg/ansibleserver/models/ansibleplaybooks.go +++ b/pkg/ansibleserver/models/ansibleplaybooks.go @@ -82,8 +82,8 @@ func init() { } func (man *SAnsiblePlaybookManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { - v := NewAnsiblePlaybookValidator("playbook", userCred) - if err := v.Validate(data); err != nil { + pbV := NewAnsiblePlaybookValidator("playbook", userCred) + if err := pbV.Validate(data); err != nil { return nil, err } data.Set("status", jsonutils.NewString(AnsiblePlaybookStatusInit)) @@ -244,6 +244,7 @@ func (apb *SAnsiblePlaybook) runPlaybook(ctx context.Context, userCred mcclient. if err != nil { apb.Status = AnsiblePlaybookStatusCanceled } else if runErr != nil { + log.Warningf("playbook %s(%s) failed: %v", apb.Name, apb.Id, runErr) apb.Status = AnsiblePlaybookStatusFailed } else { apb.Status = AnsiblePlaybookStatusSucceeded From 5ec150e7a94251889dca59c634cd3de39e29402a Mon Sep 17 00:00:00 2001 From: Yousong Zhou Date: Fri, 14 Jun 2019 10:39:25 +0000 Subject: [PATCH 2/6] =?UTF-8?q?pkg:=20ansible:=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E5=86=85=E5=AE=B9=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pkg/util/ansible/playbook.go | 19 ++++++++++++++++ pkg/util/ansible/playbook_test.go | 36 +++++++++++++++++++++++++++---- 2 files changed, 51 insertions(+), 4 deletions(-) diff --git a/pkg/util/ansible/playbook.go b/pkg/util/ansible/playbook.go index b6de3ddb67..83a57213b1 100644 --- a/pkg/util/ansible/playbook.go +++ b/pkg/util/ansible/playbook.go @@ -55,6 +55,7 @@ type Playbook struct { Inventory Inventory Modules []Module PrivateKey []byte + Files map[string][]byte tmpdir string noCleanOnExit bool @@ -77,6 +78,7 @@ func (pb *Playbook) Copy() *Playbook { pb1.Inventory = gotypes.DeepCopy(pb.Inventory).(Inventory) pb1.Modules = gotypes.DeepCopy(pb.Modules).([]Module) pb1.PrivateKey = gotypes.DeepCopy(pb.PrivateKey).([]byte) + pb1.Files = gotypes.DeepCopy(pb.Files).(map[string][]byte) return pb1 } @@ -160,6 +162,22 @@ func (pb *Playbook) Run(ctx context.Context) (err error) { } } + // write out files + for name, content := range pb.Files { + path := filepath.Join(tmpdir, "files", name) + dir := filepath.Dir(path) + err = os.MkdirAll(dir, os.FileMode(0700)) + if err != nil { + err = errors.WithMessagef(err, "mkdir -p %s", dir) + return + } + err = ioutil.WriteFile(path, content, os.FileMode(0600)) + if err != nil { + err = errors.WithMessagef(err, "writing file %s", name) + return + } + } + // run modules one by one var errs []error defer func() { @@ -185,6 +203,7 @@ func (pb *Playbook) Run(ctx context.Context) (err error) { args = append(args, "--private-key", privateKey) } cmd := exec.CommandContext(ctx, "ansible", args...) + cmd.Dir = pb.tmpdir cmd.Env = os.Environ() cmd.Env = append(cmd.Env, "ANSIBLE_HOST_KEY_CHECKING=False") stdout, _ := cmd.StdoutPipe() diff --git a/pkg/util/ansible/playbook_test.go b/pkg/util/ansible/playbook_test.go index f859dcd219..d0ebcc3616 100644 --- a/pkg/util/ansible/playbook_test.go +++ b/pkg/util/ansible/playbook_test.go @@ -17,6 +17,7 @@ package ansible import ( "context" "os/exec" + "reflect" "testing" ) @@ -45,10 +46,37 @@ func TestPlaybook(t *testing.T) { { Name: "ping", }, + { + Name: "copy", + Args: []string{ + "src=afile", + "dest=/tmp/afile", + }, + }, + { + Name: "copy", + Args: []string{ + "src=adir/afile", + "dest=/tmp/adirfile", + }, + }, } - err := pb.Run(context.TODO()) - if err != nil { - t.Fatalf("not expecting err: %v", err) + pb.Files = map[string][]byte{ + "afile": []byte("afilecontent"), + "adir/afile": []byte("afilecontent under adir"), } - t.Logf("%s", pb.Output()) + + t.Run("copy", func(t *testing.T) { + pb2 := pb.Copy() + if !reflect.DeepEqual(pb2, pb) { + t.Errorf("copy and the original should be equal") + } + }) + t.Run("run", func(t *testing.T) { + err := pb.Run(context.TODO()) + t.Logf("%s", pb.Output()) + if err != nil { + t.Fatalf("not expecting err: %v", err) + } + }) } From 2d5b52810f02a4a8e7e72b50fc9d40d7d29f4bc6 Mon Sep 17 00:00:00 2001 From: Yousong Zhou Date: Fri, 14 Jun 2019 10:52:41 +0000 Subject: [PATCH 3/6] =?UTF-8?q?mcclient:=20options:=20ansibleplaybooks:=20?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E6=8C=87=E5=AE=9A=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pkg/mcclient/options/ansibleplaybooks.go | 26 ++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/pkg/mcclient/options/ansibleplaybooks.go b/pkg/mcclient/options/ansibleplaybooks.go index e5cb7b35f7..714bb7f427 100644 --- a/pkg/mcclient/options/ansibleplaybooks.go +++ b/pkg/mcclient/options/ansibleplaybooks.go @@ -16,6 +16,8 @@ package options import ( "fmt" + "io/ioutil" + "strings" "yunion.io/x/jsonutils" @@ -33,6 +35,7 @@ type AnsiblePlaybookListOptions struct { type AnsiblePlaybookCommonOptions struct { Host []string `help:"name or id of server or host in format '<[server:]id|host:id>|ipaddr var=val'"` Mod []string `help:"ansible modules and their arguments in format 'name k1=v1 k2=v2'"` + File []string `help:"files for use by modules, e.g. name=content, name=@file"` } func (opts *AnsiblePlaybookCommonOptions) params() (jsonutils.JSONObject, error) { @@ -59,6 +62,29 @@ func (opts *AnsiblePlaybookCommonOptions) params() (jsonutils.JSONObject, error) } pb.Modules = append(pb.Modules, module) } + files := map[string][]byte{} + for _, s := range opts.File { + i := strings.IndexByte(s, '=') + if i < 0 { + return nil, fmt.Errorf("missing '=' in argument for --file. Read command help") + } + name := strings.TrimSpace(s[:i]) + if name == "" { + return nil, fmt.Errorf("empty file name: %s", s) + } + v := s[i+1:] + if len(v) > 0 && v[0] == '@' { + path := v[1:] + d, err := ioutil.ReadFile(path) + if err != nil { + return nil, fmt.Errorf("read file %s: %v", path, err) + } + files[name] = d + } else { + files[name] = []byte(v) + } + } + pb.Files = files pbJson := jsonutils.Marshal(pb) return pbJson, nil } From 85d141847f139dfccc564597a7b55a787fd511af Mon Sep 17 00:00:00 2001 From: Yousong Zhou Date: Fri, 14 Jun 2019 13:57:36 +0000 Subject: [PATCH 4/6] apis: add ansible --- pkg/apis/ansible/ansible.go | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 pkg/apis/ansible/ansible.go diff --git a/pkg/apis/ansible/ansible.go b/pkg/apis/ansible/ansible.go new file mode 100644 index 0000000000..d4090361c4 --- /dev/null +++ b/pkg/apis/ansible/ansible.go @@ -0,0 +1,29 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ansible + +import ( + "yunion.io/x/onecloud/pkg/apis" + "yunion.io/x/onecloud/pkg/util/ansible" +) + +type AnsiblePlaybookCreateInput struct { + apis.Meta + + Name string + Playbook ansible.Playbook +} + +type AnsiblePlaybookUpdateInput AnsiblePlaybookCreateInput From 68cf6698a79f4759b24d18e9a00b6471632e767b Mon Sep 17 00:00:00 2001 From: Yousong Zhou Date: Fri, 14 Jun 2019 13:59:18 +0000 Subject: [PATCH 5/6] =?UTF-8?q?mcclient:=20ansibleplaybooks:=20=E4=BD=BF?= =?UTF-8?q?=E7=94=A8apis.XxxInput?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pkg/mcclient/options/ansibleplaybooks.go | 28 ++++++++++++++---------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/pkg/mcclient/options/ansibleplaybooks.go b/pkg/mcclient/options/ansibleplaybooks.go index 714bb7f427..642bedd0d4 100644 --- a/pkg/mcclient/options/ansibleplaybooks.go +++ b/pkg/mcclient/options/ansibleplaybooks.go @@ -21,6 +21,7 @@ import ( "yunion.io/x/jsonutils" + apis "yunion.io/x/onecloud/pkg/apis/ansible" "yunion.io/x/onecloud/pkg/util/ansible" ) @@ -38,7 +39,7 @@ type AnsiblePlaybookCommonOptions struct { File []string `help:"files for use by modules, e.g. name=content, name=@file"` } -func (opts *AnsiblePlaybookCommonOptions) params() (jsonutils.JSONObject, error) { +func (opts *AnsiblePlaybookCommonOptions) ToPlaybook() (*ansible.Playbook, error) { if len(opts.Mod) == 0 { return nil, fmt.Errorf("Requires at least one --mod argument") } @@ -85,8 +86,7 @@ func (opts *AnsiblePlaybookCommonOptions) params() (jsonutils.JSONObject, error) } } pb.Files = files - pbJson := jsonutils.Marshal(pb) - return pbJson, nil + return pb, nil } type AnsiblePlaybookCreateOptions struct { @@ -95,27 +95,33 @@ type AnsiblePlaybookCreateOptions struct { } func (opts *AnsiblePlaybookCreateOptions) Params() (*jsonutils.JSONDict, error) { - pbJson, err := opts.AnsiblePlaybookCommonOptions.params() + pb, err := opts.AnsiblePlaybookCommonOptions.ToPlaybook() if err != nil { return nil, err } - params := jsonutils.NewDict() - params.Set("playbook", pbJson) - params.Set("name", jsonutils.NewString(opts.NAME)) + input := &apis.AnsiblePlaybookCreateInput{ + Name: opts.NAME, + Playbook: *pb, + } + params := input.JSON(input) return params, nil } type AnsiblePlaybookUpdateOptions struct { - ID string `json:"-" help:"name/id of the playbook"` + ID string `json:"-" help:"name/id of the playbook"` + Name string AnsiblePlaybookCommonOptions } func (opts *AnsiblePlaybookUpdateOptions) Params() (*jsonutils.JSONDict, error) { - pbJson, err := opts.AnsiblePlaybookCommonOptions.params() + pb, err := opts.AnsiblePlaybookCommonOptions.ToPlaybook() if err != nil { return nil, err } - params := jsonutils.NewDict() - params.Set("playbook", pbJson) + input := &apis.AnsiblePlaybookUpdateInput{ + Name: opts.Name, + Playbook: *pb, + } + params := input.JSON(input) return params, nil } From 36954218d5cef49ea9e13f1e88b0440558d0f0c1 Mon Sep 17 00:00:00 2001 From: Yousong Zhou Date: Fri, 14 Jun 2019 14:02:36 +0000 Subject: [PATCH 6/6] =?UTF-8?q?mcclient:=20models:=20=E6=B7=BB=E5=8A=A0ans?= =?UTF-8?q?ibleplaybooks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pkg/mcclient/models/ansibleplaybooks.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 pkg/mcclient/models/ansibleplaybooks.go diff --git a/pkg/mcclient/models/ansibleplaybooks.go b/pkg/mcclient/models/ansibleplaybooks.go new file mode 100644 index 0000000000..a2a3a9460f --- /dev/null +++ b/pkg/mcclient/models/ansibleplaybooks.go @@ -0,0 +1,16 @@ +package models + +import ( + "time" + + "yunion.io/x/onecloud/pkg/util/ansible" +) + +type AnsiblePlaybook struct { + VirtualResource + + Playbook *ansible.Playbook + Output string + StartTime time.Time + EndTime time.Time +}