blob: d5ae100c5b46c03cefa7879cea792e9d1ff941b8 [file] [log] [blame]
Scott Baker6cf525a2019-05-09 12:25:08 -07001/*
2 * Portions copyright 2019-present Open Networking Foundation
3 * Original copyright 2019-present Ciena Corporation
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17package commands
18
19import (
20 "errors"
21 "fmt"
22 flags "github.com/jessevdk/go-flags"
23 "github.com/opencord/cordctl/format"
24 "time"
25)
26
27const (
28 DEFAULT_BACKUP_FORMAT = "table{{ .Status }}\t{{ .Checksum }}\t{{ .Chunks }}\t{{ .Bytes }}"
29)
30
31type BackupOutput struct {
32 Status string `json:"status"`
33 Checksum string `json:"checksum"`
34 Chunks int `json:"chunks"`
35 Bytes int `json:"bytes"`
36}
37
38type BackupCreate struct {
39 OutputOptions
40 ChunkSize int `short:"h" long:"chunksize" default:"65536" description:"Host and port"`
41 Args struct {
42 LocalFileName string
43 } `positional-args:"yes" required:"yes"`
44}
45
46type BackupRestore struct {
47 OutputOptions
48 Args struct {
49 LocalFileName string
50 } `positional-args:"yes" required:"yes"`
51}
52
53type BackupOpts struct {
54 Create BackupCreate `command:"create"`
55 Restore BackupRestore `command:"restore"`
56}
57
58var backupOpts = BackupOpts{}
59
60func RegisterBackupCommands(parser *flags.Parser) {
61 parser.AddCommand("backup", "backup management commands", "Commands to create backups and restore backups", &backupOpts)
62}
63
64func (options *BackupCreate) Execute(args []string) error {
65 conn, descriptor, err := InitReflectionClient()
66 if err != nil {
67 return err
68 }
69 defer conn.Close()
70
71 // We might close and reopen the connection befor we do the DownloadFile,
72 // so make sure we've downloaded the service descriptor.
73 _, err = descriptor.FindSymbol("xos.filetransfer")
74 if err != nil {
75 return err
76 }
77
78 local_name := options.Args.LocalFileName
79
80 // STEP 1: Create backup operation
81 backupop := make(map[string]interface{})
82 backupop["operation"] = "create"
83 err = CreateModel(conn, descriptor, "BackupOperation", backupop)
84 if err != nil {
85 return err
86 }
87 conditional_printf(!options.Quiet, "Created backup-create operation id=%d uuid=%s\n", backupop["id"], backupop["uuid"])
88 conditional_printf(!options.Quiet, "Waiting for sync ")
89
90 // STEP 2: Wait for the operation to complete
91 flags := GM_UNTIL_ENACTED | GM_UNTIL_FOUND | Ternary_uint32(options.Quiet, GM_QUIET, 0)
92 conn, completed_backupop, err := GetModelWithRetry(conn, descriptor, "BackupOperation", backupop["id"].(int32), flags)
93 if err != nil {
94 return err
95 }
96
97 defer conn.Close()
98
99 status := completed_backupop.GetFieldByName("status").(string)
100 conditional_printf(!options.Quiet, "\nStatus: %s\n", status)
101
102 // we've failed. leave.
103 if status != "created" {
104 return errors.New("BackupOp status is " + status)
105 }
106
107 // STEP 3: Retrieve URI
108 backupfile_id := completed_backupop.GetFieldByName("file_id").(int32)
109 if backupfile_id == 0 {
110 return errors.New("BackupOp.file_id is not set")
111 }
112
113 completed_backupfile, err := GetModel(conn, descriptor, "BackupFile", backupfile_id)
114 if err != nil {
115 return err
116 }
117
118 uri := completed_backupfile.GetFieldByName("uri").(string)
119 conditional_printf(!options.Quiet, "URI %s\n", uri)
120
121 // STEP 4: Download the file
122
123 conditional_printf(!options.Quiet, "Downloading %s\n", local_name)
124
125 h, err := DownloadFile(conn, descriptor, uri, local_name)
126 if err != nil {
127 return err
128 }
129
130 // STEP 5: Show results
131 outputFormat := CharReplacer.Replace(options.Format)
132 if outputFormat == "" {
133 outputFormat = DEFAULT_BACKUP_FORMAT
134 }
135 if options.Quiet {
136 outputFormat = "{{.Status}}"
137 }
138
139 data := make([]BackupOutput, 1)
140 data[0].Chunks = h.chunks
141 data[0].Bytes = h.bytes
142 data[0].Status = h.status
143 data[0].Checksum = fmt.Sprintf("sha1:%x", h.hash.Sum(nil))
144
145 result := CommandResult{
146 Format: format.Format(outputFormat),
147 OutputAs: toOutputType(options.OutputAs),
148 Data: data,
149 }
150
151 GenerateOutput(&result)
152
153 return nil
154}
155
156func (options *BackupRestore) Execute(args []string) error {
157 conn, descriptor, err := InitReflectionClient()
158 if err != nil {
159 return err
160 }
161 defer conn.Close()
162
163 local_name := options.Args.LocalFileName
164 remote_name := "cordctl-restore-" + time.Now().Format("20060102T150405Z")
165 uri := "file:///var/run/xos/backup/local/" + remote_name
166
167 // STEP 1: Upload the file
168
169 upload_result, err := UploadFile(conn, descriptor, local_name, uri, 65536)
170 if err != nil {
171 return err
172 }
173
174 upload_status := GetEnumValue(upload_result, "status")
175 if upload_status != "SUCCESS" {
176 return errors.New("Upload status was " + upload_status)
177 }
178
179 // STEP 2: Create a BackupFile object
180 backupfile := make(map[string]interface{})
181 backupfile["name"] = remote_name
182 backupfile["uri"] = uri
183 err = CreateModel(conn, descriptor, "BackupFile", backupfile)
184 if err != nil {
185 return err
186 }
187 conditional_printf(!options.Quiet, "Created backup file %d\n", backupfile["id"])
188
189 // STEP 3: Create a BackupOperation object
190 backupop := make(map[string]interface{})
191 backupop["operation"] = "restore"
192 backupop["file_id"] = backupfile["id"]
193 err = CreateModel(conn, descriptor, "BackupOperation", backupop)
194 if err != nil {
195 return err
196 }
197 conditional_printf(!options.Quiet, "Created backup-restore operation id=%d uuid=%s\n", backupop["id"], backupop["uuid"])
198
199 conditional_printf(!options.Quiet, "Waiting for completion ")
200
201 // STEP 4: Wait for completion
202 flags := GM_UNTIL_ENACTED | GM_UNTIL_FOUND | GM_UNTIL_STATUS | Ternary_uint32(options.Quiet, GM_QUIET, 0)
203 conn, completed_backupop, err := FindModelWithRetry(conn, descriptor, "BackupOperation", "uuid", backupop["uuid"].(string), flags)
204 if err != nil {
205 return err
206 }
207
208 defer conn.Close()
209
210 conditional_printf(!options.Quiet, "\n")
211
212 // STEP 5: Show results
213 outputFormat := CharReplacer.Replace(options.Format)
214 if outputFormat == "" {
215 outputFormat = DEFAULT_BACKUP_FORMAT
216 }
217 if options.Quiet {
218 outputFormat = "{{.Status}}"
219 }
220
221 data := make([]BackupOutput, 1)
222 data[0].Checksum = upload_result.GetFieldByName("checksum").(string)
223 data[0].Chunks = int(upload_result.GetFieldByName("chunks_received").(int32))
224 data[0].Bytes = int(upload_result.GetFieldByName("bytes_received").(int32))
225
226 if completed_backupop.GetFieldByName("status") == "restored" {
227 data[0].Status = "SUCCESS"
228 } else {
229 data[0].Status = "FAILURE"
230 }
231
232 result := CommandResult{
233 Format: format.Format(outputFormat),
234 OutputAs: toOutputType(options.OutputAs),
235 Data: data,
236 }
237
238 GenerateOutput(&result)
239
240 return nil
241}