blob: 9d11db95e42210c5d72048be6d5104ebfd18ecde [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"
Scott Baker6cf525a2019-05-09 12:25:08 -070023 "time"
24)
25
26const (
27 DEFAULT_BACKUP_FORMAT = "table{{ .Status }}\t{{ .Checksum }}\t{{ .Chunks }}\t{{ .Bytes }}"
28)
29
30type BackupOutput struct {
31 Status string `json:"status"`
32 Checksum string `json:"checksum"`
33 Chunks int `json:"chunks"`
34 Bytes int `json:"bytes"`
35}
36
37type BackupCreate struct {
38 OutputOptions
39 ChunkSize int `short:"h" long:"chunksize" default:"65536" description:"Host and port"`
40 Args struct {
41 LocalFileName string
42 } `positional-args:"yes" required:"yes"`
43}
44
45type BackupRestore struct {
46 OutputOptions
47 Args struct {
48 LocalFileName string
49 } `positional-args:"yes" required:"yes"`
50}
51
52type BackupOpts struct {
53 Create BackupCreate `command:"create"`
54 Restore BackupRestore `command:"restore"`
55}
56
57var backupOpts = BackupOpts{}
58
59func RegisterBackupCommands(parser *flags.Parser) {
60 parser.AddCommand("backup", "backup management commands", "Commands to create backups and restore backups", &backupOpts)
61}
62
63func (options *BackupCreate) Execute(args []string) error {
64 conn, descriptor, err := InitReflectionClient()
65 if err != nil {
66 return err
67 }
68 defer conn.Close()
69
70 // We might close and reopen the connection befor we do the DownloadFile,
71 // so make sure we've downloaded the service descriptor.
72 _, err = descriptor.FindSymbol("xos.filetransfer")
73 if err != nil {
74 return err
75 }
76
77 local_name := options.Args.LocalFileName
78
79 // STEP 1: Create backup operation
Scott Baker72efd752019-05-15 13:12:20 -070080
Scott Baker6cf525a2019-05-09 12:25:08 -070081 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
Scott Baker72efd752019-05-15 13:12:20 -070091
Scott Baker6cf525a2019-05-09 12:25:08 -070092 flags := GM_UNTIL_ENACTED | GM_UNTIL_FOUND | Ternary_uint32(options.Quiet, GM_QUIET, 0)
93 conn, completed_backupop, err := GetModelWithRetry(conn, descriptor, "BackupOperation", backupop["id"].(int32), flags)
94 if err != nil {
95 return err
96 }
97
98 defer conn.Close()
99
100 status := completed_backupop.GetFieldByName("status").(string)
101 conditional_printf(!options.Quiet, "\nStatus: %s\n", status)
102
103 // we've failed. leave.
104 if status != "created" {
105 return errors.New("BackupOp status is " + status)
106 }
107
108 // STEP 3: Retrieve URI
109 backupfile_id := completed_backupop.GetFieldByName("file_id").(int32)
110 if backupfile_id == 0 {
111 return errors.New("BackupOp.file_id is not set")
112 }
113
114 completed_backupfile, err := GetModel(conn, descriptor, "BackupFile", backupfile_id)
115 if err != nil {
116 return err
117 }
118
119 uri := completed_backupfile.GetFieldByName("uri").(string)
120 conditional_printf(!options.Quiet, "URI %s\n", uri)
121
122 // STEP 4: Download the file
123
124 conditional_printf(!options.Quiet, "Downloading %s\n", local_name)
125
126 h, err := DownloadFile(conn, descriptor, uri, local_name)
127 if err != nil {
128 return err
129 }
130
Scott Baker72efd752019-05-15 13:12:20 -0700131 // STEP 5: Verify checksum
132
133 if completed_backupfile.GetFieldByName("checksum").(string) != h.GetChecksum() {
134 return fmt.Errorf("Checksum mismatch, received=%s, expected=%s",
135 h.GetChecksum(),
136 completed_backupfile.GetFieldByName("checksum").(string))
137 }
138
139 // STEP 6: Show results
140
Scott Baker6cf525a2019-05-09 12:25:08 -0700141 data := make([]BackupOutput, 1)
142 data[0].Chunks = h.chunks
143 data[0].Bytes = h.bytes
144 data[0].Status = h.status
Scott Baker72efd752019-05-15 13:12:20 -0700145 data[0].Checksum = h.GetChecksum()
Scott Baker6cf525a2019-05-09 12:25:08 -0700146
Scott Baker5281d002019-05-16 10:45:26 -0700147 FormatAndGenerateOutput(&options.OutputOptions, DEFAULT_BACKUP_FORMAT, "{{.Status}}", data)
Scott Baker6cf525a2019-05-09 12:25:08 -0700148
149 return nil
150}
151
152func (options *BackupRestore) Execute(args []string) error {
153 conn, descriptor, err := InitReflectionClient()
154 if err != nil {
155 return err
156 }
157 defer conn.Close()
158
159 local_name := options.Args.LocalFileName
160 remote_name := "cordctl-restore-" + time.Now().Format("20060102T150405Z")
161 uri := "file:///var/run/xos/backup/local/" + remote_name
162
163 // STEP 1: Upload the file
164
Scott Baker72efd752019-05-15 13:12:20 -0700165 h, upload_result, err := UploadFile(conn, descriptor, local_name, uri, 65536)
Scott Baker6cf525a2019-05-09 12:25:08 -0700166 if err != nil {
167 return err
168 }
169
170 upload_status := GetEnumValue(upload_result, "status")
171 if upload_status != "SUCCESS" {
172 return errors.New("Upload status was " + upload_status)
173 }
174
Scott Baker72efd752019-05-15 13:12:20 -0700175 // STEP 2: Verify checksum
176
177 if upload_result.GetFieldByName("checksum").(string) != h.GetChecksum() {
178 return fmt.Errorf("Checksum mismatch, expected=%s, received=%s",
179 h.GetChecksum(),
180 upload_result.GetFieldByName("checksum").(string))
181 }
182
Scott Baker6cf525a2019-05-09 12:25:08 -0700183 // STEP 2: Create a BackupFile object
Scott Baker72efd752019-05-15 13:12:20 -0700184
Scott Baker6cf525a2019-05-09 12:25:08 -0700185 backupfile := make(map[string]interface{})
186 backupfile["name"] = remote_name
187 backupfile["uri"] = uri
Scott Baker72efd752019-05-15 13:12:20 -0700188 backupfile["checksum"] = h.GetChecksum()
Scott Baker6cf525a2019-05-09 12:25:08 -0700189 err = CreateModel(conn, descriptor, "BackupFile", backupfile)
190 if err != nil {
191 return err
192 }
193 conditional_printf(!options.Quiet, "Created backup file %d\n", backupfile["id"])
194
195 // STEP 3: Create a BackupOperation object
Scott Baker72efd752019-05-15 13:12:20 -0700196
Scott Baker6cf525a2019-05-09 12:25:08 -0700197 backupop := make(map[string]interface{})
198 backupop["operation"] = "restore"
199 backupop["file_id"] = backupfile["id"]
200 err = CreateModel(conn, descriptor, "BackupOperation", backupop)
201 if err != nil {
202 return err
203 }
204 conditional_printf(!options.Quiet, "Created backup-restore operation id=%d uuid=%s\n", backupop["id"], backupop["uuid"])
205
206 conditional_printf(!options.Quiet, "Waiting for completion ")
207
208 // STEP 4: Wait for completion
Scott Baker72efd752019-05-15 13:12:20 -0700209
Scott Baker6cf525a2019-05-09 12:25:08 -0700210 flags := GM_UNTIL_ENACTED | GM_UNTIL_FOUND | GM_UNTIL_STATUS | Ternary_uint32(options.Quiet, GM_QUIET, 0)
Scott Baker5201c0b2019-05-15 15:35:56 -0700211 queries := map[string]string{"uuid": backupop["uuid"].(string)}
212 conn, completed_backupop, err := FindModelWithRetry(conn, descriptor, "BackupOperation", queries, flags)
Scott Baker6cf525a2019-05-09 12:25:08 -0700213 if err != nil {
214 return err
215 }
216
217 defer conn.Close()
218
219 conditional_printf(!options.Quiet, "\n")
220
221 // STEP 5: Show results
Scott Baker72efd752019-05-15 13:12:20 -0700222
Scott Baker6cf525a2019-05-09 12:25:08 -0700223 data := make([]BackupOutput, 1)
224 data[0].Checksum = upload_result.GetFieldByName("checksum").(string)
225 data[0].Chunks = int(upload_result.GetFieldByName("chunks_received").(int32))
226 data[0].Bytes = int(upload_result.GetFieldByName("bytes_received").(int32))
227
228 if completed_backupop.GetFieldByName("status") == "restored" {
229 data[0].Status = "SUCCESS"
230 } else {
231 data[0].Status = "FAILURE"
232 }
233
Scott Baker5281d002019-05-16 10:45:26 -0700234 FormatAndGenerateOutput(&options.OutputOptions, DEFAULT_BACKUP_FORMAT, "{{.Status}}", data)
Scott Baker6cf525a2019-05-09 12:25:08 -0700235
236 return nil
237}