blob: a49698f2f1fb83aa8c2a8e31866b4c170902ff46 [file] [log] [blame]
mpagenkoc8bba412021-01-15 15:38:44 +00001/*
2 * Copyright 2020-present Open Networking Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17//Package adaptercoreonu provides the utility for onu devices, flows and statistics
18package adaptercoreonu
19
20import (
mpagenko80622a52021-02-09 16:53:23 +000021 "bufio"
mpagenkoc8bba412021-01-15 15:38:44 +000022 "context"
mpagenko15ff4a52021-03-02 10:09:20 +000023 "errors"
mpagenko02cf1b22021-03-12 17:30:30 +000024 "io"
25 "net/http"
26 "net/url"
mpagenko80622a52021-02-09 16:53:23 +000027 "os"
mpagenkoc8bba412021-01-15 15:38:44 +000028 "sync"
mpagenko02cf1b22021-03-12 17:30:30 +000029 "time"
mpagenkoc8bba412021-01-15 15:38:44 +000030
31 "github.com/opencord/voltha-protos/v4/go/voltha"
32
33 "github.com/opencord/voltha-lib-go/v4/pkg/log"
34)
35
36// ### downloadToAdapter related definitions ####
37
38//not yet defined to go with sca..., later also some configure options ??
39//const defaultDownloadTimeout = 60 // (?) Seconds
40//const localImgPath = "/home/lcui/work/tmp"
41
42// ### downloadToAdapter - end ####
43
44//adapterDownloadManager structure holds information needed for downloading to and storing images within the adapter
45type adapterDownloadManager struct {
46 mutexDownloadImageDsc sync.RWMutex
47 downloadImageDscSlice []*voltha.ImageDownload
mpagenko80622a52021-02-09 16:53:23 +000048 // maybe just for test purpose
49 arrayFileFragment [32]byte
mpagenkoc8bba412021-01-15 15:38:44 +000050}
51
52//newAdapterDownloadManager constructor returns a new instance of a adapterDownloadManager
53//mib_db (as well as not inluded alarm_db not really used in this code? VERIFY!!)
54func newAdapterDownloadManager(ctx context.Context) *adapterDownloadManager {
55 logger.Debug(ctx, "init-adapterDownloadManager")
56 var localDnldMgr adapterDownloadManager
57 localDnldMgr.downloadImageDscSlice = make([]*voltha.ImageDownload, 0)
mpagenkoc8bba412021-01-15 15:38:44 +000058 return &localDnldMgr
59}
60
61//imageExists returns true if the requested image already exists within the adapter
62func (dm *adapterDownloadManager) imageExists(ctx context.Context, apImageDsc *voltha.ImageDownload) bool {
63 logger.Debugw(ctx, "checking on existence of the image", log.Fields{"image-name": (*apImageDsc).Name})
64 dm.mutexDownloadImageDsc.RLock()
65 defer dm.mutexDownloadImageDsc.RUnlock()
66
67 for _, pDnldImgDsc := range dm.downloadImageDscSlice {
68 if (*pDnldImgDsc).Name == (*apImageDsc).Name {
69 //image found (by name)
70 return true
71 }
72 }
73 //image not found (by name)
74 return false
75}
76
mpagenko80622a52021-02-09 16:53:23 +000077//imageLocallyDownloaded returns true if the requested image already exists within the adapter
78func (dm *adapterDownloadManager) imageLocallyDownloaded(ctx context.Context, apImageDsc *voltha.ImageDownload) bool {
79 logger.Debugw(ctx, "checking if image is fully downloaded", log.Fields{"image-name": (*apImageDsc).Name})
80 dm.mutexDownloadImageDsc.RLock()
81 defer dm.mutexDownloadImageDsc.RUnlock()
82
83 for _, pDnldImgDsc := range dm.downloadImageDscSlice {
84 if (*pDnldImgDsc).Name == (*apImageDsc).Name {
85 //image found (by name)
86 if (*pDnldImgDsc).DownloadState == voltha.ImageDownload_DOWNLOAD_SUCCEEDED {
87 logger.Debugw(ctx, "image has been fully downloaded", log.Fields{"image-name": (*apImageDsc).Name})
88 return true
89 }
90 logger.Debugw(ctx, "image not yet fully downloaded", log.Fields{"image-name": (*apImageDsc).Name})
91 return false
92 }
93 }
94 //image not found (by name)
95 logger.Errorw(ctx, "image does not exist", log.Fields{"image-name": (*apImageDsc).Name})
96 return false
97}
98
mpagenkoc8bba412021-01-15 15:38:44 +000099//startDownload returns true if the download of the requested image could be started
100func (dm *adapterDownloadManager) startDownload(ctx context.Context, apImageDsc *voltha.ImageDownload) error {
mpagenko15ff4a52021-03-02 10:09:20 +0000101 if apImageDsc.LocalDir != "" {
mpagenko02cf1b22021-03-12 17:30:30 +0000102 logger.Infow(ctx, "image download-to-adapter requested", log.Fields{
103 "image-path": apImageDsc.LocalDir, "image-name": apImageDsc.Name})
mpagenko15ff4a52021-03-02 10:09:20 +0000104 newImageDscPos := len(dm.downloadImageDscSlice)
105 dm.downloadImageDscSlice = append(dm.downloadImageDscSlice, apImageDsc)
106 dm.downloadImageDscSlice[newImageDscPos].DownloadState = voltha.ImageDownload_DOWNLOAD_STARTED
mpagenko02cf1b22021-03-12 17:30:30 +0000107 if apImageDsc.LocalDir == "/intern" {
108 //just for initial 'internal' test verification
109 //just some basic test file simulation
110 dm.downloadImageDscSlice[newImageDscPos].LocalDir = "/tmp"
111 go dm.writeFileToLFS(ctx, "/tmp", apImageDsc.Name)
112 return nil
113 } else if apImageDsc.LocalDir == "/reboot" {
114 dm.downloadImageDscSlice[newImageDscPos].LocalDir = "/tmp"
115 }
116 //try to download from http
117 urlName := apImageDsc.Url + "/" + apImageDsc.Name
118 go dm.downloadFile(ctx, urlName, apImageDsc.LocalDir, apImageDsc.Name)
mpagenko15ff4a52021-03-02 10:09:20 +0000119 //return success to comfort the core processing during integration
120 return nil
121 }
122 // we can use the missing local path temporary also to test some failure behavior (system reation on failure)
123 // with updated control API's or at some adequate time we could also set some defined fixed localPath internally
124 logger.Errorw(ctx, "could not start download: no valid local directory to write to", log.Fields{"image-name": (*apImageDsc).Name})
125 return errors.New("could not start download: no valid local directory to write to")
mpagenkoc8bba412021-01-15 15:38:44 +0000126}
mpagenko80622a52021-02-09 16:53:23 +0000127
mpagenko02cf1b22021-03-12 17:30:30 +0000128//downloadFile downloads the specified file from the given http location
129func (dm *adapterDownloadManager) downloadFile(ctx context.Context, aURLName string, aFilePath string, aFileName string) {
130 // Get the data
131 logger.Infow(ctx, "downloading from http", log.Fields{"url": aURLName, "localPath": aFilePath})
132 // http command is already part of the aURLName argument
133 urlBase, err1 := url.Parse(aURLName)
134 if err1 != nil {
135 logger.Errorw(ctx, "could not set base url command", log.Fields{"url": aURLName, "error": err1})
136 }
137 urlParams := url.Values{}
138 urlBase.RawQuery = urlParams.Encode()
139 req, err2 := http.NewRequest("GET", urlBase.String(), nil)
140 if err2 != nil {
141 logger.Errorw(ctx, "could not generate http request", log.Fields{"url": urlBase.String(), "error": err2})
142 return
143 }
144 ctx, cancel := context.WithDeadline(ctx, time.Now().Add(10*time.Second)) //timeout to be discussed
145 defer cancel()
146 _ = req.WithContext(ctx)
147
148 resp, err3 := http.DefaultClient.Do(req)
149 if err3 != nil {
150 logger.Errorw(ctx, "could not http get from url", log.Fields{"url": urlBase.String(), "error": err3})
151 return
152 }
153 defer func() {
154 deferredErr := resp.Body.Close()
155 if deferredErr != nil {
156 logger.Errorw(ctx, "error at closing http response body", log.Fields{"url": urlBase.String(), "error": deferredErr})
157 }
158 }()
159
160 // Create the file
161 aLocalPathName := aFilePath + "/" + aFileName
162 file, err := os.Create(aLocalPathName)
163 if err != nil {
164 logger.Errorw(ctx, "could not create local file", log.Fields{"path_file": aLocalPathName, "error": err})
165 return
166 }
167 defer func() {
168 deferredErr := file.Close()
169 if deferredErr != nil {
170 logger.Errorw(ctx, "error at closing new file", log.Fields{"path_file": aLocalPathName, "error": deferredErr})
171 }
172 }()
173
174 // Write the body to file
175 _, err = io.Copy(file, resp.Body)
176 if err != nil {
177 logger.Errorw(ctx, "could not copy file content", log.Fields{"url": urlBase.String(), "file": aLocalPathName, "error": err})
178 return
179 }
180
181 fileStats, statsErr := file.Stat()
182 if err != nil {
183 logger.Errorw(ctx, "created file can't be accessed", log.Fields{"file": aLocalPathName, "stat-error": statsErr})
184 }
185 logger.Infow(ctx, "written file size is", log.Fields{"file": aLocalPathName, "length": fileStats.Size()})
186
187 for _, pDnldImgDsc := range dm.downloadImageDscSlice {
188 if (*pDnldImgDsc).Name == aFileName {
189 //image found (by name)
190 (*pDnldImgDsc).DownloadState = voltha.ImageDownload_DOWNLOAD_SUCCEEDED
191 return //can leave directly
192 }
193 }
194}
195
mpagenko80622a52021-02-09 16:53:23 +0000196//writeFileToLFS writes the downloaded file to the local file system
mpagenko02cf1b22021-03-12 17:30:30 +0000197// this is just an internal test function and can be removed if other download capabilities exist
198func (dm *adapterDownloadManager) writeFileToLFS(ctx context.Context, aLocalPath string, aFileName string) {
mpagenko80622a52021-02-09 16:53:23 +0000199 // by now just a simulation to write a file with predefined 'variable' content
200 totalFileLength := 0
mpagenko15ff4a52021-03-02 10:09:20 +0000201 logger.Debugw(ctx, "writing fixed size simulation file locally", log.Fields{
mpagenko80622a52021-02-09 16:53:23 +0000202 "image-name": aFileName, "image-path": aLocalPath})
203 file, err := os.Create(aLocalPath + "/" + aFileName)
204 if err == nil {
205 // write 32KB test file
206 for totalFileLength < 32*1024 {
207 if written, wrErr := file.Write(dm.getIncrementalSliceContent(ctx)); wrErr == nil {
208 totalFileLength += written
209 } else {
mpagenko15ff4a52021-03-02 10:09:20 +0000210 logger.Errorw(ctx, "could not write to file", log.Fields{"create-error": wrErr})
mpagenko80622a52021-02-09 16:53:23 +0000211 break //stop writing
212 }
213 }
214 } else {
mpagenko15ff4a52021-03-02 10:09:20 +0000215 logger.Errorw(ctx, "could not create file", log.Fields{"create-error": err})
mpagenko80622a52021-02-09 16:53:23 +0000216 }
217
218 fileStats, statsErr := file.Stat()
219 if err != nil {
220 logger.Errorw(ctx, "created file can't be accessed", log.Fields{"stat-error": statsErr})
221 }
mpagenko15ff4a52021-03-02 10:09:20 +0000222 logger.Infow(ctx, "written file size is", log.Fields{"file": aLocalPath + "/" + aFileName, "length": fileStats.Size()})
mpagenko02cf1b22021-03-12 17:30:30 +0000223
224 defer func() {
225 deferredErr := file.Close()
226 if deferredErr != nil {
227 logger.Errorw(ctx, "error at closing test file", log.Fields{"file": aLocalPath + "/" + aFileName, "error": deferredErr})
228 }
229 }()
mpagenko80622a52021-02-09 16:53:23 +0000230
231 for _, pDnldImgDsc := range dm.downloadImageDscSlice {
232 if (*pDnldImgDsc).Name == aFileName {
233 //image found (by name)
234 (*pDnldImgDsc).DownloadState = voltha.ImageDownload_DOWNLOAD_SUCCEEDED
235 return //can leave directly
236 }
237 }
238}
239
240//getImageBufferLen returns the length of the specified file in bytes (file size)
241func (dm *adapterDownloadManager) getImageBufferLen(ctx context.Context, aFileName string,
242 aLocalPath string) (int64, error) {
243 //maybe we can also use FileSize from dm.downloadImageDscSlice - future option?
244
245 //nolint:gosec
246 file, err := os.Open(aLocalPath + "/" + aFileName)
247 if err != nil {
248 return 0, err
249 }
250 //nolint:errcheck
251 defer file.Close()
252
253 stats, statsErr := file.Stat()
254 if statsErr != nil {
255 return 0, statsErr
256 }
257
258 return stats.Size(), nil
259}
260
261//getDownloadImageBuffer returns the content of the requested file as byte slice
262func (dm *adapterDownloadManager) getDownloadImageBuffer(ctx context.Context, aFileName string,
263 aLocalPath string) ([]byte, error) {
264 //nolint:gosec
265 file, err := os.Open(aLocalPath + "/" + aFileName)
266 if err != nil {
267 return nil, err
268 }
269 //nolint:errcheck
270 defer file.Close()
271
272 stats, statsErr := file.Stat()
273 if statsErr != nil {
274 return nil, statsErr
275 }
276
277 var size int64 = stats.Size()
278 bytes := make([]byte, size)
279
280 buffer := bufio.NewReader(file)
281 _, err = buffer.Read(bytes)
282
283 return bytes, err
284}
285
286//getIncrementalSliceContent returns a byte slice of incremented bytes of internal array (used for file emulation)
287// (used for file emulation)
288func (dm *adapterDownloadManager) getIncrementalSliceContent(ctx context.Context) []byte {
289 lastValue := dm.arrayFileFragment[len(dm.arrayFileFragment)-1]
290 for index := range dm.arrayFileFragment {
291 lastValue++
292 dm.arrayFileFragment[index] = lastValue
293 }
294 return dm.arrayFileFragment[:]
295}