ONU SW upgrade API change - step 1 to allow for straightforward upgrade (with activate/commit options)
Signed-off-by: mpagenko <michael.pagenkopf@adtran.com>
Change-Id: I6808f8f41db40faa060ed7198025abdda8506ae7
diff --git a/internal/pkg/onuadaptercore/device_handler.go b/internal/pkg/onuadaptercore/device_handler.go
index 07fa67b..fc29ac2 100644
--- a/internal/pkg/onuadaptercore/device_handler.go
+++ b/internal/pkg/onuadaptercore/device_handler.go
@@ -1100,6 +1100,182 @@
return err
}
+//onuSwUpgradeAfterDownload initiates the SW download transfer to the ONU with activate and commit options
+// after the OnuImage has been downloaded to the adapter, called in background
+func (dh *deviceHandler) onuSwUpgradeAfterDownload(ctx context.Context, apImageRequest *voltha.DeviceImageDownloadRequest,
+ apDownloadManager *fileDownloadManager, aImageIdentifier string) {
+
+ var err error
+ pDevEntry := dh.getOnuDeviceEntry(ctx, true)
+ if pDevEntry == nil {
+ logger.Errorw(ctx, "start Onu SW upgrade rejected: no valid OnuDevice", log.Fields{"device-id": dh.deviceID})
+ return
+ }
+
+ var inactiveImageID uint16
+ if inactiveImageID, err = pDevEntry.GetInactiveImageMeID(ctx); err == nil {
+ logger.Debugw(ctx, "onuSwUpgrade requested", log.Fields{
+ "device-id": dh.deviceID, "image-version": apImageRequest.Image.Version, "to onu-image": inactiveImageID})
+ dh.lockUpgradeFsm.Lock()
+ defer dh.lockUpgradeFsm.Unlock()
+ if dh.pOnuUpradeFsm == nil {
+ //OmciOnuSwUpgradeDone could be used to create some Kafka event with information on upgrade completion,
+ // but none yet defined
+ err = dh.createOnuUpgradeFsm(ctx, pDevEntry, OmciOnuSwUpgradeDone)
+ if err == nil {
+ if err = dh.pOnuUpradeFsm.SetDownloadParamsAfterDownload(ctx, inactiveImageID,
+ apImageRequest, apDownloadManager, aImageIdentifier, dh.pOpenOnuAc.dlToOnuTimeout4M); err != nil {
+ logger.Errorw(ctx, "onu upgrade fsm could not set parameters", log.Fields{
+ "device-id": dh.deviceID, "error": err})
+ return
+ }
+ } else {
+ logger.Errorw(ctx, "onu upgrade fsm could not be created", log.Fields{
+ "device-id": dh.deviceID, "error": err})
+ }
+ return
+ }
+ //OnuSw upgrade already running - restart (with possible abort of running)
+ logger.Debugw(ctx, "Onu SW upgrade already running - abort", log.Fields{"device-id": dh.deviceID})
+ pUpgradeStatemachine := dh.pOnuUpradeFsm.pAdaptFsm.pFsm
+ if pUpgradeStatemachine != nil {
+ if err = pUpgradeStatemachine.Event(upgradeEvAbort); err != nil {
+ logger.Errorw(ctx, "onu upgrade fsm could not abort a running processing", log.Fields{
+ "device-id": dh.deviceID, "error": err})
+ return
+ }
+ //TODO!!!: wait for 'ready' to start and configure - see above SetDownloadParams()
+ // for now a second start of download should work again - must still be initiated by user
+ } else { //should never occur
+ logger.Errorw(ctx, "onu upgrade fsm inconsistent setup", log.Fields{
+ "device-id": dh.deviceID})
+ }
+ return
+ }
+ logger.Errorw(ctx, "start Onu SW upgrade rejected: no inactive image", log.Fields{
+ "device-id": dh.deviceID, "error": err})
+}
+
+//onuSwActivateRequest ensures activation of the requested image with commit options
+func (dh *deviceHandler) onuSwActivateRequest(ctx context.Context, aVersion string, aCommitRequest bool) {
+ var err error
+ //SW activation for the ONU image may have two use cases, one of them is selected here according to following prioritization:
+ // 1.) activation of the image for a started upgrade process (in case the running upgrade runs on the requested image)
+ // 2.) activation of the inactive image
+
+ pDevEntry := dh.getOnuDeviceEntry(ctx, true)
+ if pDevEntry == nil {
+ logger.Errorw(ctx, "Onu image activation rejected: no valid OnuDevice", log.Fields{"device-id": dh.deviceID})
+ return
+ }
+ dh.lockUpgradeFsm.RLock()
+ if dh.pOnuUpradeFsm != nil {
+ dh.lockUpgradeFsm.RUnlock()
+ onuVolthaDevice, getErr := dh.coreProxy.GetDevice(log.WithSpanFromContext(context.TODO(), ctx),
+ dh.deviceID, dh.deviceID)
+ if getErr != nil || onuVolthaDevice == nil {
+ logger.Errorw(ctx, "Failed to fetch Onu device for image activation", log.Fields{"device-id": dh.deviceID, "err": getErr})
+ return
+ }
+ // use the OnuVendor identification from this device for the internal unique name
+ imageIdentifier := onuVolthaDevice.VendorId + aVersion //head on vendor ID of the ONU
+ // 1.) check a started upgrade process and rely the activation request to it
+ if err = dh.pOnuUpradeFsm.SetActivationParamsRunning(ctx, imageIdentifier, aCommitRequest); err != nil {
+ logger.Errorw(ctx, "onu upgrade fsm did not accept activation while running", log.Fields{
+ "device-id": dh.deviceID, "error": err})
+ } else {
+ logger.Debugw(ctx, "image activation acknowledged by onu upgrade processing", log.Fields{
+ "device-id": dh.deviceID, "image-id": imageIdentifier})
+ }
+ //if some ONU upgrade is ongoing we do not accept some explicit ONU image-version related activation
+ // (even though parameter setting is not accepted)
+ return
+ } //else
+ dh.lockUpgradeFsm.RUnlock()
+
+ // 2.) check if requested image-version equals the inactive one and start its activation
+ // (image version is not [yet] checked - would be possible, but with increased effort ...)
+ var inactiveImageID uint16
+ if inactiveImageID, err = pDevEntry.GetInactiveImageMeID(ctx); err != nil || inactiveImageID > 1 {
+ logger.Errorw(ctx, "get inactive image failed", log.Fields{
+ "device-id": dh.deviceID, "err": err, "image-id": inactiveImageID})
+ return
+ }
+ err = dh.createOnuUpgradeFsm(ctx, pDevEntry, OmciOnuSwUpgradeDone)
+ if err == nil {
+ if err = dh.pOnuUpradeFsm.SetActivationParamsStart(ctx, aVersion,
+ inactiveImageID, aCommitRequest); err != nil {
+ logger.Errorw(ctx, "onu upgrade fsm did not accept activation to start", log.Fields{
+ "device-id": dh.deviceID, "error": err})
+ return
+ }
+ logger.Debugw(ctx, "inactive image activation acknowledged by onu upgrade", log.Fields{
+ "device-id": dh.deviceID, "image-version": aVersion})
+ return
+ } //else
+ logger.Errorw(ctx, "onu upgrade fsm could not be created", log.Fields{
+ "device-id": dh.deviceID, "error": err})
+}
+
+//onuSwCommitRequest ensures commitment of the requested image
+func (dh *deviceHandler) onuSwCommitRequest(ctx context.Context, aVersion string) {
+ var err error
+ //SW commitment for the ONU image may have two use cases, one of them is selected here according to following prioritization:
+ // 1.) commitment of the image for a started upgrade process (in case the running upgrade runs on the requested image)
+ // 2.) commitment of the active image
+
+ pDevEntry := dh.getOnuDeviceEntry(ctx, true)
+ if pDevEntry == nil {
+ logger.Errorw(ctx, "Onu image commitment rejected: no valid OnuDevice", log.Fields{"device-id": dh.deviceID})
+ return
+ }
+ dh.lockUpgradeFsm.RLock()
+ if dh.pOnuUpradeFsm != nil {
+ dh.lockUpgradeFsm.RUnlock()
+ onuVolthaDevice, getErr := dh.coreProxy.GetDevice(log.WithSpanFromContext(context.TODO(), ctx),
+ dh.deviceID, dh.deviceID)
+ if getErr != nil || onuVolthaDevice == nil {
+ logger.Errorw(ctx, "Failed to fetch Onu device for image commitment", log.Fields{"device-id": dh.deviceID, "err": getErr})
+ return
+ }
+ // use the OnuVendor identification from this device for the internal unique name
+ imageIdentifier := onuVolthaDevice.VendorId + aVersion //head on vendor ID of the ONU
+ // 1.) check a started upgrade process and rely the commitment request to it
+ if err = dh.pOnuUpradeFsm.SetCommitmentParamsRunning(ctx, imageIdentifier); err != nil {
+ logger.Errorw(ctx, "onu upgrade fsm did not accept commitment while running", log.Fields{
+ "device-id": dh.deviceID, "error": err})
+ } else {
+ logger.Debugw(ctx, "image commitment acknowledged by onu upgrade processing", log.Fields{
+ "device-id": dh.deviceID, "image-id": imageIdentifier})
+ }
+ //if some ONU upgrade is ongoing we do not accept some explicit ONU image-version related commitment
+ // (even though parameter setting is not accepted)
+ return
+ } //else
+ dh.lockUpgradeFsm.RUnlock()
+
+ // 2.) check if requested image-version equals the inactive one and start its commitment
+ var activeImageID uint16
+ if activeImageID, err = pDevEntry.GetActiveImageMeID(ctx); err != nil || activeImageID > 1 {
+ logger.Errorw(ctx, "get active image failed", log.Fields{
+ "device-id": dh.deviceID, "err": err, "image-id": activeImageID})
+ return
+ }
+ err = dh.createOnuUpgradeFsm(ctx, pDevEntry, OmciOnuSwUpgradeDone)
+ if err == nil {
+ if err = dh.pOnuUpradeFsm.SetCommitmentParamsStart(ctx, aVersion, activeImageID); err != nil {
+ logger.Errorw(ctx, "onu upgrade fsm did not accept commitment to start", log.Fields{
+ "device-id": dh.deviceID, "error": err})
+ return
+ }
+ logger.Debugw(ctx, "active image commitment acknowledged by onu upgrade", log.Fields{
+ "device-id": dh.deviceID, "image-version": aVersion})
+ return
+ } //else
+ logger.Errorw(ctx, "onu upgrade fsm could not be created", log.Fields{
+ "device-id": dh.deviceID, "error": err})
+}
+
// deviceHandler methods that implement the adapters interface requests## end #########
// #####################################################################################
@@ -1731,18 +1907,10 @@
}
//reset a possibly running upgrade FSM
- // specific here: If the FSM is in upgradeStWaitForCommit, it is left there for possibly later commit
- // this possibly also refers later to (not yet existing) upgradeStWaitForActivate (with ctl API changes)
+ // (note the Upgrade FSM may stay alive e.g. in state upgradeStWaitForCommit to endure the ONU reboot)
dh.lockUpgradeFsm.RLock()
if dh.pOnuUpradeFsm != nil {
- pUpgradeStatemachine := dh.pOnuUpradeFsm.pAdaptFsm.pFsm
- if pUpgradeStatemachine != nil {
- if pUpgradeStatemachine.Is(upgradeStWaitEndDL) {
- dh.pOnuUpradeFsm.chReceiveExpectedResponse <- false //which aborts the FSM (activate was not yet sent)
- }
- _ = pUpgradeStatemachine.Event(upgradeEvReset) //anyway and for all other states
- }
- //else the FSM seems already to be in some released state
+ dh.pOnuUpradeFsm.CancelProcessing(ctx)
}
dh.lockUpgradeFsm.RUnlock()
diff --git a/internal/pkg/onuadaptercore/file_download_manager.go b/internal/pkg/onuadaptercore/file_download_manager.go
new file mode 100644
index 0000000..0658c6f
--- /dev/null
+++ b/internal/pkg/onuadaptercore/file_download_manager.go
@@ -0,0 +1,350 @@
+/*
+ * Copyright 2020-present Open Networking Foundation
+ *
+ * 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 adaptercoreonu provides the utility for onu devices, flows and statistics
+package adaptercoreonu
+
+import (
+ "bufio"
+ "context"
+ "fmt"
+ "io"
+ "net/http"
+ "net/url"
+ "os"
+ "sync"
+ "time"
+
+ "github.com/opencord/voltha-lib-go/v4/pkg/log"
+)
+
+const cDefaultLocalDir = "/tmp" //this is the default local dir to download to
+
+type fileState uint32
+
+//nolint:varcheck, deadcode
+const (
+ cFileStateUnknown fileState = iota
+ cFileStateDlStarted
+ cFileStateDlSucceeded
+ cFileStateDlFailed
+ cFileStateDlAborted
+ cFileStateDlInvalid
+)
+
+type downloadImageParams struct {
+ downloadImageName string
+ downloadImageState fileState
+ downloadImageLen int64
+ downloadImageCrc uint32
+}
+
+type requesterChannelMap map[chan<- bool]struct{} //using an empty structure map for easier (unique) element appending
+
+//fileDownloadManager structure holds information needed for downloading to and storing images within the adapter
+type fileDownloadManager struct {
+ mutexDownloadImageDsc sync.RWMutex
+ downloadImageDscSlice []downloadImageParams
+ dnldImgReadyWaiting map[string]requesterChannelMap
+ dlToAdapterTimeout time.Duration
+}
+
+//newFileDownloadManager constructor returns a new instance of a fileDownloadManager
+//mib_db (as well as not inluded alarm_db not really used in this code? VERIFY!!)
+func newFileDownloadManager(ctx context.Context) *fileDownloadManager {
+ logger.Debug(ctx, "init-fileDownloadManager")
+ var localDnldMgr fileDownloadManager
+ localDnldMgr.downloadImageDscSlice = make([]downloadImageParams, 0)
+ localDnldMgr.dnldImgReadyWaiting = make(map[string]requesterChannelMap)
+ localDnldMgr.dlToAdapterTimeout = 10 * time.Second //default timeout, should be overwritten immediately after start
+ return &localDnldMgr
+}
+
+//SetDownloadTimeout configures the timeout used to supervice the download of the image to the adapter (assumed in seconds)
+func (dm *fileDownloadManager) SetDownloadTimeout(ctx context.Context, aDlTimeout time.Duration) {
+ dm.mutexDownloadImageDsc.Lock()
+ defer dm.mutexDownloadImageDsc.Unlock()
+ logger.Debugw(ctx, "setting download timeout", log.Fields{"timeout": aDlTimeout})
+ dm.dlToAdapterTimeout = aDlTimeout
+}
+
+//GetDownloadTimeout delivers the timeout used to supervice the download of the image to the adapter (assumed in seconds)
+func (dm *fileDownloadManager) GetDownloadTimeout(ctx context.Context) time.Duration {
+ dm.mutexDownloadImageDsc.RLock()
+ defer dm.mutexDownloadImageDsc.RUnlock()
+ return dm.dlToAdapterTimeout
+}
+
+//ImageExists returns true if the requested image already exists within the adapter
+func (dm *fileDownloadManager) ImageExists(ctx context.Context, aImageName string) bool {
+ logger.Debugw(ctx, "checking on existence of the image", log.Fields{"image-name": aImageName})
+ dm.mutexDownloadImageDsc.RLock()
+ defer dm.mutexDownloadImageDsc.RUnlock()
+
+ for _, dnldImgDsc := range dm.downloadImageDscSlice {
+ if dnldImgDsc.downloadImageName == aImageName {
+ //image found (by name)
+ return true
+ }
+ }
+ //image not found (by name)
+ return false
+}
+
+//StartDownload returns true if the download of the requested image could be started for the given file name and URL
+func (dm *fileDownloadManager) StartDownload(ctx context.Context, aImageName string, aURLCommand string) error {
+ logger.Infow(ctx, "image download-to-adapter requested", log.Fields{
+ "image-name": aImageName, "url-command": aURLCommand})
+ loDownloadImageParams := downloadImageParams{
+ downloadImageName: aImageName, downloadImageState: cFileStateDlStarted,
+ downloadImageLen: 0, downloadImageCrc: 0}
+ dm.mutexDownloadImageDsc.Lock()
+ dm.downloadImageDscSlice = append(dm.downloadImageDscSlice, loDownloadImageParams)
+ dm.mutexDownloadImageDsc.Unlock()
+ //try to download from http
+ err := dm.downloadFile(ctx, aURLCommand, cDefaultLocalDir, aImageName)
+ //return the result of the start-request to comfort the core processing even though the complete download may go on in background
+ return err
+}
+
+//GetImageBufferLen returns the length of the specified file in bytes (file size) - as detected after download
+func (dm *fileDownloadManager) GetImageBufferLen(ctx context.Context, aFileName string) (int64, error) {
+ dm.mutexDownloadImageDsc.RLock()
+ defer dm.mutexDownloadImageDsc.RUnlock()
+ for _, dnldImgDsc := range dm.downloadImageDscSlice {
+ if dnldImgDsc.downloadImageName == aFileName && dnldImgDsc.downloadImageState == cFileStateDlSucceeded {
+ //image found (by name) and fully downloaded
+ return dnldImgDsc.downloadImageLen, nil
+ }
+ }
+ return 0, fmt.Errorf("no downloaded image found: %s", aFileName)
+}
+
+//GetDownloadImageBuffer returns the content of the requested file as byte slice
+func (dm *fileDownloadManager) GetDownloadImageBuffer(ctx context.Context, aFileName string) ([]byte, error) {
+ //nolint:gosec
+ file, err := os.Open(cDefaultLocalDir + "/" + aFileName)
+ if err != nil {
+ return nil, err
+ }
+ //nolint:errcheck
+ defer file.Close()
+
+ stats, statsErr := file.Stat()
+ if statsErr != nil {
+ return nil, statsErr
+ }
+
+ var size int64 = stats.Size()
+ bytes := make([]byte, size)
+
+ buffer := bufio.NewReader(file)
+ _, err = buffer.Read(bytes)
+
+ return bytes, err
+}
+
+//RequestDownloadReady receives a channel that has to be used to inform the requester in case the concerned file is downloaded
+func (dm *fileDownloadManager) RequestDownloadReady(ctx context.Context, aFileName string, aWaitChannel chan<- bool) {
+ if dm.imageLocallyDownloaded(ctx, aFileName) {
+ //image found (by name) and fully downloaded
+ logger.Debugw(ctx, "file ready - immediate response", log.Fields{"image-name": aFileName})
+ aWaitChannel <- true
+ return
+ }
+ //when we are here the image was not yet found or not fully downloaded -
+ // add the device specific channel to the list of waiting requesters
+ dm.mutexDownloadImageDsc.Lock()
+ defer dm.mutexDownloadImageDsc.Unlock()
+ if loRequesterChannelMap, ok := dm.dnldImgReadyWaiting[aFileName]; ok {
+ //entry for the file name already exists
+ if _, exists := loRequesterChannelMap[aWaitChannel]; !exists {
+ // requester channel does not yet exist for the image
+ loRequesterChannelMap[aWaitChannel] = struct{}{}
+ dm.dnldImgReadyWaiting[aFileName] = loRequesterChannelMap
+ logger.Debugw(ctx, "file not ready - adding new requester", log.Fields{
+ "image-name": aFileName, "number-of-requesters": len(dm.dnldImgReadyWaiting[aFileName])})
+ }
+ } else {
+ //entry for the file name does not even exist
+ addRequesterChannelMap := make(map[chan<- bool]struct{})
+ addRequesterChannelMap[aWaitChannel] = struct{}{}
+ dm.dnldImgReadyWaiting[aFileName] = addRequesterChannelMap
+ logger.Debugw(ctx, "file not ready - setting first requester", log.Fields{
+ "image-name": aFileName})
+ }
+}
+
+//RemoveReadyRequest removes the specified channel from the requester(channel) map for the given file name
+func (dm *fileDownloadManager) RemoveReadyRequest(ctx context.Context, aFileName string, aWaitChannel chan bool) {
+ dm.mutexDownloadImageDsc.Lock()
+ defer dm.mutexDownloadImageDsc.Unlock()
+ for imageName, channelMap := range dm.dnldImgReadyWaiting {
+ if imageName == aFileName {
+ for channel := range channelMap {
+ if channel == aWaitChannel {
+ delete(dm.dnldImgReadyWaiting[imageName], channel)
+ logger.Debugw(ctx, "channel removed from the requester map", log.Fields{
+ "image-name": aFileName, "new number-of-requesters": len(dm.dnldImgReadyWaiting[aFileName])})
+ return //can leave directly
+ }
+ }
+ return //can leave directly
+ }
+ }
+}
+
+// FileDownloadManager private (unexported) methods -- start
+
+//imageLocallyDownloaded returns true if the requested image already exists within the adapter
+func (dm *fileDownloadManager) imageLocallyDownloaded(ctx context.Context, aImageName string) bool {
+ logger.Debugw(ctx, "checking if image is fully downloaded to adapter", log.Fields{"image-name": aImageName})
+ dm.mutexDownloadImageDsc.RLock()
+ defer dm.mutexDownloadImageDsc.RUnlock()
+
+ for _, dnldImgDsc := range dm.downloadImageDscSlice {
+ if dnldImgDsc.downloadImageName == aImageName {
+ //image found (by name)
+ if dnldImgDsc.downloadImageState == cFileStateDlSucceeded {
+ logger.Debugw(ctx, "image has been fully downloaded", log.Fields{"image-name": aImageName})
+ return true
+ }
+ logger.Debugw(ctx, "image not yet fully downloaded", log.Fields{"image-name": aImageName})
+ return false
+ }
+ }
+ //image not found (by name)
+ logger.Errorw(ctx, "image does not exist", log.Fields{"image-name": aImageName})
+ return false
+}
+
+//downloadFile downloads the specified file from the given http location
+func (dm *fileDownloadManager) downloadFile(ctx context.Context, aURLCommand string, aFilePath string, aFileName string) error {
+ // Get the data
+ logger.Infow(ctx, "downloading with URL", log.Fields{"url": aURLCommand, "localPath": aFilePath})
+ // verifying the complete URL by parsing it to its URL elements
+ urlBase, err1 := url.Parse(aURLCommand)
+ if err1 != nil {
+ logger.Errorw(ctx, "could not set base url command", log.Fields{"url": aURLCommand, "error": err1})
+ return fmt.Errorf("could not set base url command: %s, error: %s", aURLCommand, err1)
+ }
+ urlParams := url.Values{}
+ urlBase.RawQuery = urlParams.Encode()
+
+ //pre-check on file existence - assuming http location here
+ reqExist, errExist2 := http.NewRequest("HEAD", urlBase.String(), nil)
+ if errExist2 != nil {
+ logger.Errorw(ctx, "could not generate http head request", log.Fields{"url": urlBase.String(), "error": errExist2})
+ return fmt.Errorf("could not generate http head request: %s, error: %s", aURLCommand, errExist2)
+ }
+ ctxExist, cancelExist := context.WithDeadline(ctx, time.Now().Add(3*time.Second)) //waiting for some fast answer
+ defer cancelExist()
+ _ = reqExist.WithContext(ctxExist)
+ respExist, errExist3 := http.DefaultClient.Do(reqExist)
+ if errExist3 != nil || respExist.StatusCode != http.StatusOK {
+ logger.Infow(ctx, "could not http head from url", log.Fields{"url": urlBase.String(),
+ "error": errExist3, "status": respExist.StatusCode})
+ //if head is not supported by server we cannot use this test and just try to continue
+ if respExist.StatusCode != http.StatusMethodNotAllowed {
+ logger.Errorw(ctx, "http head from url: file does not exist here, aborting", log.Fields{"url": urlBase.String(),
+ "error": errExist3, "status": respExist.StatusCode})
+ return fmt.Errorf("http head from url: file does not exist here, aborting: %s, error: %s, status: %d",
+ aURLCommand, errExist2, respExist.StatusCode)
+ }
+ }
+ defer func() {
+ deferredErr := respExist.Body.Close()
+ if deferredErr != nil {
+ logger.Errorw(ctx, "error at closing http head response body", log.Fields{"url": urlBase.String(), "error": deferredErr})
+ }
+ }()
+
+ //trying to download - do it in background as it may take some time ...
+ go func() {
+ req, err2 := http.NewRequest("GET", urlBase.String(), nil)
+ if err2 != nil {
+ logger.Errorw(ctx, "could not generate http request", log.Fields{"url": urlBase.String(), "error": err2})
+ return
+ }
+ ctx, cancel := context.WithDeadline(ctx, time.Now().Add(dm.dlToAdapterTimeout)) //timeout as given from SetDownloadTimeout()
+ defer cancel()
+ _ = req.WithContext(ctx)
+ resp, err3 := http.DefaultClient.Do(req)
+ if err3 != nil || respExist.StatusCode != http.StatusOK {
+ logger.Errorw(ctx, "could not http get from url", log.Fields{"url": urlBase.String(),
+ "error": err3, "status": respExist.StatusCode})
+ return
+ }
+ defer func() {
+ deferredErr := resp.Body.Close()
+ if deferredErr != nil {
+ logger.Errorw(ctx, "error at closing http get response body", log.Fields{"url": urlBase.String(), "error": deferredErr})
+ }
+ }()
+
+ // Create the file
+ aLocalPathName := aFilePath + "/" + aFileName
+ file, err := os.Create(aLocalPathName)
+ if err != nil {
+ logger.Errorw(ctx, "could not create local file", log.Fields{"path_file": aLocalPathName, "error": err})
+ return
+ }
+ defer func() {
+ deferredErr := file.Close()
+ if deferredErr != nil {
+ logger.Errorw(ctx, "error at closing new file", log.Fields{"path_file": aLocalPathName, "error": deferredErr})
+ }
+ }()
+
+ // Write the body to file
+ _, err = io.Copy(file, resp.Body)
+ if err != nil {
+ logger.Errorw(ctx, "could not copy file content", log.Fields{"url": urlBase.String(), "file": aLocalPathName, "error": err})
+ return
+ }
+
+ fileStats, statsErr := file.Stat()
+ if err != nil {
+ logger.Errorw(ctx, "created file can't be accessed", log.Fields{"file": aLocalPathName, "stat-error": statsErr})
+ }
+ fileSize := fileStats.Size()
+ logger.Infow(ctx, "written file size is", log.Fields{"file": aLocalPathName, "length": fileSize})
+
+ dm.mutexDownloadImageDsc.Lock()
+ defer dm.mutexDownloadImageDsc.Unlock()
+ for imgKey, dnldImgDsc := range dm.downloadImageDscSlice {
+ if dnldImgDsc.downloadImageName == aFileName {
+ //image found (by name) - need to write changes on the original map
+ dm.downloadImageDscSlice[imgKey].downloadImageState = cFileStateDlSucceeded
+ dm.downloadImageDscSlice[imgKey].downloadImageLen = fileSize
+ //in case upgrade process(es) was/were waiting for the file, inform them
+ for imageName, channelMap := range dm.dnldImgReadyWaiting {
+ if imageName == aFileName {
+ for channel := range channelMap {
+ // use all found channels to inform possible requesters about the existence of the file
+ channel <- true
+ delete(dm.dnldImgReadyWaiting[imageName], channel) //requester served
+ }
+ return //can leave directly
+ }
+ }
+ return //can leave directly
+ }
+ }
+ //TODO:!!! further extension could be provided here, e.g. already computing and possibly comparing the CRC, vendor check
+ }()
+ return nil
+}
diff --git a/internal/pkg/onuadaptercore/omci_cc.go b/internal/pkg/onuadaptercore/omci_cc.go
index f371379..0e3b8a5 100644
--- a/internal/pkg/onuadaptercore/omci_cc.go
+++ b/internal/pkg/onuadaptercore/omci_cc.go
@@ -492,13 +492,15 @@
func (oo *omciCC) send(ctx context.Context, txFrame []byte, timeout int, retry int, highPrio bool,
receiveCallbackPair callbackPair) error {
- logger.Debugw(ctx, "register-response-callback:", log.Fields{"for TansCorrId": receiveCallbackPair.cbKey})
- // it could be checked, if the callback keay is already registered - but simply overwrite may be acceptable ...
- oo.mutexRxSchedMap.Lock()
- oo.rxSchedulerMap[receiveCallbackPair.cbKey] = receiveCallbackPair.cbEntry
- printFrame := receiveCallbackPair.cbEntry.framePrint //printFrame true means debug print of frame is requested
- oo.mutexRxSchedMap.Unlock()
+ if timeout != 0 {
+ logger.Debugw(ctx, "register-response-callback:", log.Fields{"for TansCorrId": receiveCallbackPair.cbKey})
+ oo.mutexRxSchedMap.Lock()
+ // it could be checked, if the callback key is already registered - but simply overwrite may be acceptable ...
+ oo.rxSchedulerMap[receiveCallbackPair.cbKey] = receiveCallbackPair.cbEntry
+ oo.mutexRxSchedMap.Unlock()
+ } //else timeout 0 indicates that no response is expected - fire and forget
+ printFrame := receiveCallbackPair.cbEntry.framePrint //printFrame true means debug print of frame is requested
//just use a simple list for starting - might need some more effort, especially for multi source write access
omciTxRequest := omciTransferStructure{
txFrame,
@@ -585,15 +587,6 @@
proxy_device_id=self._proxy_address.device_id
)
*/
- device, err := oo.coreProxy.GetDevice(ctx,
- oo.pBaseDeviceHandler.deviceID, oo.deviceID) //parent, child
- if err != nil || device == nil {
- /*TODO: needs to handle error scenarios */
- logger.Errorw(ctx, "Failed to fetch device", log.Fields{"err": err, "ParentId": oo.pBaseDeviceHandler.deviceID,
- "ChildId": oo.deviceID})
- return fmt.Errorf("failed to fetch device %s", oo.deviceID)
- }
-
if omciTxRequest.withFramePrint {
logger.Debugw(ctx, "omci-message-to-send:", log.Fields{
"TxOmciMessage": hex.EncodeToString(omciTxRequest.txFrame),
@@ -2706,7 +2699,7 @@
return nil
}
-func (oo *omciCC) sendDownloadSection(ctx context.Context, timeout int, highPrio bool,
+func (oo *omciCC) sendDownloadSection(ctx context.Context, aTimeout int, highPrio bool,
rxChan chan Message, aImageMeID uint16, aAckRequest uint8, aDownloadSectionNo uint8, aSection []byte, aPrint bool) error {
tid := oo.getNextTid(highPrio)
logger.Debugw(ctx, "send DlSectionRequest:", log.Fields{"device-id": oo.deviceID,
@@ -2716,8 +2709,10 @@
//TODO!!!: don't know by now on how to generate the possibly needed AR (or enforce it to 0) with current omci-lib
// by now just try to send it as defined by omci-lib
msgType := omci.DownloadSectionRequestType
+ var timeout int = 0 //default value for no response expected
if aAckRequest > 0 {
msgType = omci.DownloadSectionRequestWithResponseType
+ timeout = aTimeout
}
omciLayer := &omci.OMCI{
TransactionID: tid,
@@ -2754,6 +2749,8 @@
}
omciRxCallbackPair := callbackPair{cbKey: tid,
+ // the callback is set even though no response might be required here, the tid (key) setting is needed here anyway
+ // (used to avoid retransmission of frames with the same TID)
cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse, printFrame /*aPrint*/},
}
err = oo.send(ctx, outgoingPacket, timeout, cDefaultRetries, highPrio, omciRxCallbackPair)
@@ -3014,52 +3011,58 @@
func (oo *omciCC) processRequestMonitoring(ctx context.Context, aOmciTxRequest omciTransferStructure) {
- chSuccess := make(chan bool)
- aOmciTxRequest.chSuccess = chSuccess
-
- tid := aOmciTxRequest.cbPair.cbKey
timeout := aOmciTxRequest.timeout
- retries := aOmciTxRequest.retries
-
- oo.mutexMonReq.Lock()
- oo.monitoredRequests[tid] = aOmciTxRequest
- oo.mutexMonReq.Unlock()
-
- retryCounter := 0
-loop:
- for retryCounter <= retries {
-
+ if timeout == 0 {
+ //timeout 0 indicates that no response is expected - fire and forget
oo.mutexTxQueue.Lock()
oo.txQueue.PushBack(aOmciTxRequest) // enqueue
oo.mutexTxQueue.Unlock()
-
go oo.sendNextRequest(ctx)
+ } else {
+ chSuccess := make(chan bool)
+ aOmciTxRequest.chSuccess = chSuccess
+ tid := aOmciTxRequest.cbPair.cbKey
+ retries := aOmciTxRequest.retries
- select {
- case success := <-chSuccess:
- if success {
- logger.Debugw(ctx, "reqMon: response received in time",
- log.Fields{"tid": tid, "device-id": oo.deviceID})
- } else {
- logger.Debugw(ctx, "reqMon: wait for response aborted",
- log.Fields{"tid": tid, "device-id": oo.deviceID})
- }
- break loop
- case <-time.After(time.Duration(timeout) * time.Second):
- if retryCounter == retries {
- logger.Errorw(ctx, "reqMon: timeout waiting for response - no of max retries reached!",
- log.Fields{"tid": tid, "retries": retryCounter, "device-id": oo.deviceID})
+ oo.mutexMonReq.Lock()
+ oo.monitoredRequests[tid] = aOmciTxRequest
+ oo.mutexMonReq.Unlock()
+
+ retryCounter := 0
+ loop:
+ for retryCounter <= retries {
+
+ oo.mutexTxQueue.Lock()
+ oo.txQueue.PushBack(aOmciTxRequest) // enqueue
+ oo.mutexTxQueue.Unlock()
+ go oo.sendNextRequest(ctx)
+
+ select {
+ case success := <-chSuccess:
+ if success {
+ logger.Debugw(ctx, "reqMon: response received in time",
+ log.Fields{"tid": tid, "device-id": oo.deviceID})
+ } else {
+ logger.Debugw(ctx, "reqMon: wait for response aborted",
+ log.Fields{"tid": tid, "device-id": oo.deviceID})
+ }
break loop
- } else {
- logger.Infow(ctx, "reqMon: timeout waiting for response - retry",
- log.Fields{"tid": tid, "retries": retryCounter, "device-id": oo.deviceID})
+ case <-time.After(time.Duration(timeout) * time.Second):
+ if retryCounter == retries {
+ logger.Errorw(ctx, "reqMon: timeout waiting for response - no of max retries reached!",
+ log.Fields{"tid": tid, "retries": retryCounter, "device-id": oo.deviceID})
+ break loop
+ } else {
+ logger.Infow(ctx, "reqMon: timeout waiting for response - retry",
+ log.Fields{"tid": tid, "retries": retryCounter, "device-id": oo.deviceID})
+ }
}
+ retryCounter++
}
- retryCounter++
+ oo.mutexMonReq.Lock()
+ delete(oo.monitoredRequests, tid)
+ oo.mutexMonReq.Unlock()
}
- oo.mutexMonReq.Lock()
- delete(oo.monitoredRequests, tid)
- oo.mutexMonReq.Unlock()
}
//CancelRequestMonitoring terminates monitoring of outstanding omci requests
diff --git a/internal/pkg/onuadaptercore/omci_onu_upgrade.go b/internal/pkg/onuadaptercore/omci_onu_upgrade.go
index 58e78a4..1e542ce 100644
--- a/internal/pkg/onuadaptercore/omci_onu_upgrade.go
+++ b/internal/pkg/onuadaptercore/omci_onu_upgrade.go
@@ -19,8 +19,10 @@
import (
"context"
+ "encoding/binary"
"fmt"
"strconv"
+ "sync"
"time"
"github.com/boguslaw-wojcik/crc32a"
@@ -38,16 +40,17 @@
cOmciDownloadSectionSize = 31 //in bytes
cOmciDownloadWindowSizeLimit = 31 //in sections for window offset (windowSize(32)-1)
//cOmciDownloadWindowRetryMax = 2 // max attempts for a specific window
- cOmciSectionInterleaveMilliseconds = 100 //DownloadSection interleave time in milliseconds
- cOmciEndSwDlDelaySeconds = 1 //End Software Download delay after last section (may be also configurable?)
- cWaitCountEndSwDl = 6 //maximum number of EndSwDl requests
- cWaitDelayEndSwDlSeconds = 10 //duration, how long is waited before next request on EndSwDl
+ cOmciSectionInterleaveMilliseconds = 0 //DownloadSection interleave time in milliseconds (0 for no delay)
+ cOmciEndSwDlDelaySeconds = 1 //End Software Download delay after last section (may be also configurable?)
+ cWaitCountEndSwDl = 6 //maximum number of EndSwDl requests
+ cWaitDelayEndSwDlSeconds = 10 //duration, how long is waited before next request on EndSwDl
//cOmciDownloadCompleteTimeout = 5400 //in s for the complete timeout (may be better scale to image size/ noOfWindows)
)
const (
// events of config PON ANI port FSM
upgradeEvStart = "upgradeEvStart"
+ upgradeEvAdapterDownload = "upgradeEvAdapterDownload"
upgradeEvPrepareSwDownload = "upgradeEvPrepareSwDownload"
upgradeEvRxStartSwDownload = "upgradeEvRxStartSwDownload"
upgradeEvWaitWindowAck = "upgradeEvWaitWindowAck"
@@ -55,6 +58,7 @@
upgradeEvEndSwDownload = "upgradeEvEndSwDownload"
upgradeEvWaitEndDownload = "upgradeEvWaitEndDownload"
upgradeEvContinueFinalize = "upgradeEvContinueFinalize"
+ upgradeEvWaitForActivate = "upgradeEvWaitForActivate"
upgradeEvRequestActivate = "upgradeEvRequestActivate"
upgradeEvWaitForCommit = "upgradeEvWaitForCommit"
upgradeEvCommitSw = "upgradeEvCommitSw"
@@ -71,11 +75,13 @@
// states of config PON ANI port FSM
upgradeStDisabled = "upgradeStDisabled"
upgradeStStarting = "upgradeStStarting"
+ upgradeStWaitingAdapterDL = "upgradeStWaitingAdapterDL"
upgradeStPreparingDL = "upgradeStPreparingDL"
upgradeStDLSection = "upgradeStDLSection"
upgradeStVerifyWindow = "upgradeStVerifyWindow"
upgradeStFinalizeDL = "upgradeStFinalizeDL"
upgradeStWaitEndDL = "upgradeStWaitEndDL"
+ upgradeStWaitForActivate = "upgradeStWaitForActivate"
upgradeStRequestingActivate = "upgradeStRequestingActivate"
upgradeStWaitForCommit = "upgradeStWaitForCommit"
upgradeStCommitSw = "upgradeStCommitSw"
@@ -90,32 +96,46 @@
type OnuUpgradeFsm struct {
pDeviceHandler *deviceHandler
pDownloadManager *adapterDownloadManager
+ pFileManager *fileDownloadManager //used from R2.8 with new API version
deviceID string
pOnuOmciDevice *OnuDeviceEntry
pOmciCC *omciCC
pOnuDB *onuDeviceDB
requestEvent OnuDeviceEvent
//omciMIdsResponseReceived chan bool //seperate channel needed for checking multiInstance OMCI message responses
- pAdaptFsm *AdapterFsm
- pImageDsc *voltha.ImageDownload
- imageBuffer []byte
- origImageLength uint32 //as also limited by OMCI
- imageCRC uint32 //as per OMCI - ITU I.363.5 crc
- imageLength uint32 //including last bytes padding
- omciDownloadWindowSizeLimit uint8 //windowSize-1 in sections
- omciDownloadWindowSizeLast uint8 //number of sections in last window
- noOfSections uint32 //uint32 range for sections should be sufficient for very long images
- nextDownloadSectionsAbsolute uint32 //number of next section to download in overall image
- nextDownloadSectionsWindow uint8 //number of next section to download within current window
- noOfWindows uint32 //uint32 range for windows should be sufficient for very long images
- nextDownloadWindow uint32 //number of next window to download
- inactiveImageMeID uint16 //ME-ID of the inactive image
- omciSectionInterleaveDelay time.Duration //DownloadSectionInterleave delay in milliseconds
- delayEndSwDl bool //flag to provide a delay between last section and EndSwDl
- pLastTxMeInstance *me.ManagedEntity
- waitCountEndSwDl uint8 //number, how often is waited for EndSwDl at maximum
- waitDelayEndSwDl time.Duration //duration, how long is waited before next request on EndSwDl
- chReceiveExpectedResponse chan bool
+ pAdaptFsm *AdapterFsm
+ pImageDsc *voltha.ImageDownload
+ imageBuffer []byte
+ origImageLength uint32 //as also limited by OMCI
+ imageCRC uint32 //as per OMCI - ITU I.363.5 crc
+ imageLength uint32 //including last bytes padding
+ omciDownloadWindowSizeLimit uint8 //windowSize-1 in sections
+ omciDownloadWindowSizeLast uint8 //number of sections in last window
+ noOfSections uint32 //uint32 range for sections should be sufficient for very long images
+ nextDownloadSectionsAbsolute uint32 //number of next section to download in overall image
+ nextDownloadSectionsWindow uint8 //number of next section to download within current window
+ noOfWindows uint32 //uint32 range for windows should be sufficient for very long images
+ nextDownloadWindow uint32 //number of next window to download
+ inactiveImageMeID uint16 //ME-ID of the inactive image
+ downloadToOnuTimeout4MB time.Duration //timeout for downloading the image to the ONU for a 4MB image slice
+ omciSectionInterleaveDelay time.Duration //DownloadSectionInterleave delay in milliseconds
+ delayEndSwDl bool //flag to provide a delay between last section and EndSwDl
+ pLastTxMeInstance *me.ManagedEntity
+ waitCountEndSwDl uint8 //number, how often is waited for EndSwDl at maximum
+ waitDelayEndSwDl time.Duration //duration, how long is waited before next request on EndSwDl
+ chReceiveExpectedResponse chan bool
+ useAPIVersion43 bool //flag for indication on which API version is used (and accordingly which specific methods)
+ mutexUpgradeParams sync.RWMutex
+ imageVersion string //name of the image as used within OMCI (and on extrenal API interface)
+ imageIdentifier string //name of the image as used in the adapter
+ mutexIsAwaitingAdapterDlResponse sync.RWMutex
+ chAdapterDlReady chan bool
+ isWaitingForAdapterDlResponse bool
+ mutexIsAwaitingOnuDlResponse sync.RWMutex
+ chOnuDlReady chan bool
+ isWaitingForOnuDlResponse bool
+ activateImage bool
+ commitImage bool
}
//NewOnuUpgradeFsm is the 'constructor' for the state machine to config the PON ANI ports
@@ -136,6 +156,8 @@
waitDelayEndSwDl: cWaitDelayEndSwDlSeconds,
}
instFsm.chReceiveExpectedResponse = make(chan bool)
+ instFsm.chAdapterDlReady = make(chan bool)
+ instFsm.chOnuDlReady = make(chan bool)
instFsm.pAdaptFsm = NewAdapterFsm(aName, instFsm.deviceID, aCommChannel)
if instFsm.pAdaptFsm == nil {
@@ -147,17 +169,20 @@
upgradeStDisabled,
fsm.Events{
{Name: upgradeEvStart, Src: []string{upgradeStDisabled}, Dst: upgradeStStarting},
- {Name: upgradeEvPrepareSwDownload, Src: []string{upgradeStStarting}, Dst: upgradeStPreparingDL},
+ {Name: upgradeEvAdapterDownload, Src: []string{upgradeStStarting}, Dst: upgradeStWaitingAdapterDL},
+ {Name: upgradeEvPrepareSwDownload, Src: []string{upgradeStStarting, upgradeStWaitingAdapterDL}, Dst: upgradeStPreparingDL},
{Name: upgradeEvRxStartSwDownload, Src: []string{upgradeStPreparingDL}, Dst: upgradeStDLSection},
{Name: upgradeEvWaitWindowAck, Src: []string{upgradeStDLSection}, Dst: upgradeStVerifyWindow},
{Name: upgradeEvContinueNextWindow, Src: []string{upgradeStVerifyWindow}, Dst: upgradeStDLSection},
{Name: upgradeEvEndSwDownload, Src: []string{upgradeStVerifyWindow}, Dst: upgradeStFinalizeDL},
{Name: upgradeEvWaitEndDownload, Src: []string{upgradeStFinalizeDL}, Dst: upgradeStWaitEndDL},
{Name: upgradeEvContinueFinalize, Src: []string{upgradeStWaitEndDL}, Dst: upgradeStFinalizeDL},
- {Name: upgradeEvRequestActivate, Src: []string{upgradeStWaitEndDL}, Dst: upgradeStRequestingActivate},
+ {Name: upgradeEvWaitForActivate, Src: []string{upgradeStWaitEndDL}, Dst: upgradeStWaitForActivate},
+ {Name: upgradeEvRequestActivate, Src: []string{upgradeStStarting, upgradeStWaitEndDL, upgradeStWaitForActivate},
+ Dst: upgradeStRequestingActivate}, //allows also for direct activation (without download) [TODO!!!]
{Name: upgradeEvWaitForCommit, Src: []string{upgradeStRequestingActivate}, Dst: upgradeStWaitForCommit},
{Name: upgradeEvCommitSw, Src: []string{upgradeStStarting, upgradeStWaitForCommit},
- Dst: upgradeStCommitSw},
+ Dst: upgradeStCommitSw}, //allows also for direct commitment (without download) [TODO!!!]
{Name: upgradeEvCheckCommitted, Src: []string{upgradeStCommitSw}, Dst: upgradeStCheckCommitted},
/*
@@ -167,19 +192,20 @@
upgradeStCreatingGemNCTPs, upgradeStCreatingGemIWs, upgradeStSettingPQs}, Dst: upgradeStStarting},
*/
// exceptional treatments
- {Name: upgradeEvReset, Src: []string{upgradeStStarting, upgradeStPreparingDL, upgradeStDLSection,
- upgradeStVerifyWindow, upgradeStDLSection, upgradeStFinalizeDL, upgradeStWaitEndDL, upgradeStRequestingActivate,
- upgradeStCommitSw, upgradeStCheckCommitted}, //upgradeStWaitForCommit is not reset (later perhaps also not upgradeStWaitActivate)
+ {Name: upgradeEvReset, Src: []string{upgradeStStarting, upgradeStWaitingAdapterDL, upgradeStPreparingDL, upgradeStDLSection,
+ upgradeStVerifyWindow, upgradeStDLSection, upgradeStFinalizeDL, upgradeStWaitEndDL, upgradeStWaitForActivate,
+ upgradeStRequestingActivate, upgradeStCommitSw, upgradeStCheckCommitted}, //upgradeStWaitForCommit is not reset (later perhaps also not upgradeStWaitActivate)
Dst: upgradeStResetting},
- {Name: upgradeEvAbort, Src: []string{upgradeStStarting, upgradeStPreparingDL, upgradeStDLSection,
- upgradeStVerifyWindow, upgradeStDLSection, upgradeStFinalizeDL, upgradeStWaitEndDL, upgradeStRequestingActivate,
- upgradeStWaitForCommit, upgradeStCommitSw, upgradeStCheckCommitted},
+ {Name: upgradeEvAbort, Src: []string{upgradeStStarting, upgradeStWaitingAdapterDL, upgradeStPreparingDL, upgradeStDLSection,
+ upgradeStVerifyWindow, upgradeStDLSection, upgradeStFinalizeDL, upgradeStWaitEndDL, upgradeStWaitForActivate,
+ upgradeStRequestingActivate, upgradeStWaitForCommit, upgradeStCommitSw, upgradeStCheckCommitted},
Dst: upgradeStResetting},
{Name: upgradeEvRestart, Src: []string{upgradeStResetting}, Dst: upgradeStDisabled},
},
fsm.Callbacks{
"enter_state": func(e *fsm.Event) { instFsm.pAdaptFsm.logFsmStateChange(ctx, e) },
"enter_" + upgradeStStarting: func(e *fsm.Event) { instFsm.enterStarting(ctx, e) },
+ "enter_" + upgradeStWaitingAdapterDL: func(e *fsm.Event) { instFsm.enterWaitingAdapterDL(ctx, e) },
"enter_" + upgradeStPreparingDL: func(e *fsm.Event) { instFsm.enterPreparingDL(ctx, e) },
"enter_" + upgradeStDLSection: func(e *fsm.Event) { instFsm.enterDownloadSection(ctx, e) },
"enter_" + upgradeStVerifyWindow: func(e *fsm.Event) { instFsm.enterVerifyWindow(ctx, e) },
@@ -203,6 +229,7 @@
}
//SetDownloadParams configures the needed parameters for a specific download to the ONU
+// called from 'old' API Activate_image_update()
func (oFsm *OnuUpgradeFsm) SetDownloadParams(ctx context.Context, aInactiveImageID uint16,
apImageDsc *voltha.ImageDownload, apDownloadManager *adapterDownloadManager) error {
pBaseFsm := oFsm.pAdaptFsm.pFsm
@@ -214,7 +241,7 @@
oFsm.pDownloadManager = apDownloadManager
go func(aPBaseFsm *fsm.FSM) {
- // let the upgrade FSm proceed to PreparinDL
+ // let the upgrade FSM proceed to PreparingDL
_ = aPBaseFsm.Event(upgradeEvPrepareSwDownload)
}(pBaseFsm)
return nil
@@ -224,6 +251,193 @@
return fmt.Errorf(fmt.Sprintf("OnuUpgradeFsm abort: invalid FSM base pointer or state for device-id: %s", oFsm.deviceID))
}
+//SetDownloadParamsAfterDownload configures the needed parameters for a specific download to the ONU according to
+// updated API interface with R2.8: start download to ONU if the image is downloaded to the adapter
+// called from 'new' API Download_onu_image
+func (oFsm *OnuUpgradeFsm) SetDownloadParamsAfterDownload(ctx context.Context, aInactiveImageID uint16,
+ apImageRequest *voltha.DeviceImageDownloadRequest, apDownloadManager *fileDownloadManager,
+ aImageIdentifier string, aDownloadTimeout time.Duration) error {
+ oFsm.mutexUpgradeParams.Lock()
+ var pBaseFsm *fsm.FSM = nil
+ if oFsm.pAdaptFsm != nil {
+ pBaseFsm = oFsm.pAdaptFsm.pFsm
+ }
+ if pBaseFsm != nil && pBaseFsm.Is(upgradeStStarting) {
+ logger.Debugw(ctx, "OnuUpgradeFsm Parameter setting", log.Fields{
+ "device-id": oFsm.deviceID, "image-description": apImageRequest})
+ oFsm.useAPIVersion43 = true
+ oFsm.inactiveImageMeID = aInactiveImageID //upgrade state machines run on configured inactive ImageId
+ oFsm.pFileManager = apDownloadManager
+ oFsm.imageIdentifier = aImageIdentifier
+ oFsm.imageVersion = apImageRequest.Image.Version
+ oFsm.activateImage = apImageRequest.ActivateOnSuccess
+ oFsm.commitImage = apImageRequest.CommitOnSuccess
+ oFsm.downloadToOnuTimeout4MB = aDownloadTimeout
+ //TODO: currently straightforward options activate and commit are expected to be set and (unconditionally) done
+ // for separate handling of these options the FSM must accordingly branch from the concerned states - later
+ oFsm.mutexUpgradeParams.Unlock()
+ _ = pBaseFsm.Event(upgradeEvAdapterDownload) //no need to call the FSM event in background here
+ return nil
+ }
+ oFsm.mutexUpgradeParams.Unlock()
+ logger.Errorw(ctx, "OnuUpgradeFsm abort: invalid FSM base pointer or state", log.Fields{
+ "device-id": oFsm.deviceID})
+ return fmt.Errorf(fmt.Sprintf("OnuUpgradeFsm abort: invalid FSM base pointer or state for device-id: %s", oFsm.deviceID))
+}
+
+//SetActivationParamsRunning sets the activate and commit flags for a running download to the ONU according to adapters rpc call
+// called from 'new' API Activate_onu_image
+func (oFsm *OnuUpgradeFsm) SetActivationParamsRunning(ctx context.Context,
+ aImageIdentifier string, aCommit bool) error {
+ oFsm.mutexUpgradeParams.Lock()
+ //set activate/commit independent from state, if FSM is already beyond concerned states, then it does not matter anyway
+ // (as long as the Imageidentifier is correct)
+ logger.Debugw(ctx, "OnuUpgradeFsm activate/commit parameter setting", log.Fields{
+ "device-id": oFsm.deviceID, "image-id": aImageIdentifier, "commit": aCommit})
+ if aImageIdentifier != oFsm.imageIdentifier {
+ logger.Errorw(ctx, "OnuUpgradeFsm abort: mismatching upgrade image", log.Fields{
+ "device-id": oFsm.deviceID, "request-image": aImageIdentifier, "fsm-image": oFsm.imageIdentifier})
+ oFsm.mutexUpgradeParams.Unlock()
+ return fmt.Errorf(fmt.Sprintf("OnuUpgradeFsm params ignored: requested image-name not used in current upgrade for device-id: %s",
+ oFsm.deviceID))
+ }
+ oFsm.activateImage = true
+ oFsm.commitImage = aCommit
+ oFsm.mutexUpgradeParams.Unlock()
+ var pBaseFsm *fsm.FSM = nil
+ if oFsm.pAdaptFsm != nil {
+ pBaseFsm = oFsm.pAdaptFsm.pFsm
+ }
+ if pBaseFsm != nil {
+ if pBaseFsm.Is(upgradeStWaitForActivate) {
+ logger.Debugw(ctx, "OnuUpgradeFsm finish waiting for activate", log.Fields{"device-id": oFsm.deviceID})
+ _ = pBaseFsm.Event(upgradeEvRequestActivate) //no need to call the FSM event in background here
+ }
+ return nil
+ }
+ logger.Errorw(ctx, "OnuUpgradeFsm abort: invalid FSM base pointer", log.Fields{
+ "device-id": oFsm.deviceID})
+ return fmt.Errorf(fmt.Sprintf("OnuUpgradeFsm abort: invalid FSM base pointer for device-id: %s", oFsm.deviceID))
+}
+
+//SetActivationParamsStart starts upgrade processing with immediate activation
+// called from 'new' API Activate_onu_image
+func (oFsm *OnuUpgradeFsm) SetActivationParamsStart(ctx context.Context, aImageVersion string, aInactiveImageID uint16, aCommit bool) error {
+ oFsm.mutexUpgradeParams.Lock()
+ var pBaseFsm *fsm.FSM = nil
+ if oFsm.pAdaptFsm != nil {
+ pBaseFsm = oFsm.pAdaptFsm.pFsm
+ }
+ if pBaseFsm != nil && pBaseFsm.Is(upgradeStStarting) {
+ logger.Debugw(ctx, "OnuUpgradeFsm Parameter setting to start with activation", log.Fields{
+ "device-id": oFsm.deviceID, "image-version": aImageVersion})
+ oFsm.useAPIVersion43 = true
+ oFsm.inactiveImageMeID = aInactiveImageID //upgrade state machines run on configured inactive ImageId
+ oFsm.imageVersion = aImageVersion
+ oFsm.activateImage = true
+ oFsm.commitImage = aCommit
+ oFsm.mutexUpgradeParams.Unlock()
+ //directly request the FSM to activate the image
+ _ = pBaseFsm.Event(upgradeEvRequestActivate) //no need to call the FSM event in background here
+ return nil
+ }
+ oFsm.mutexUpgradeParams.Unlock()
+ logger.Errorw(ctx, "OnuUpgradeFsm abort: invalid FSM base pointer or state", log.Fields{
+ "device-id": oFsm.deviceID})
+ return fmt.Errorf(fmt.Sprintf("OnuUpgradeFsm abort: invalid FSM base pointer or state for device-id: %s", oFsm.deviceID))
+}
+
+//SetCommitmentParamsRunning sets the commit flag for a running download to the ONU according to adapters rpc call
+// called from 'new' API Commit_onu_image
+func (oFsm *OnuUpgradeFsm) SetCommitmentParamsRunning(ctx context.Context, aImageIdentifier string) error {
+ oFsm.mutexUpgradeParams.Lock()
+ //set commit independent from state, if FSM is already beyond commit state (just ready), then it does not matter anyway
+ // (as long as the Imageidentifier is correct)
+ logger.Debugw(ctx, "OnuUpgradeFsm commit parameter setting", log.Fields{
+ "device-id": oFsm.deviceID, "image-id": aImageIdentifier})
+ if aImageIdentifier != oFsm.imageIdentifier {
+ logger.Errorw(ctx, "OnuUpgradeFsm abort: mismatching upgrade image", log.Fields{
+ "device-id": oFsm.deviceID, "request-image": aImageIdentifier, "fsm-image": oFsm.imageIdentifier})
+ oFsm.mutexUpgradeParams.Unlock()
+ return fmt.Errorf(fmt.Sprintf("OnuUpgradeFsm params ignored: requested image-name not used in current upgrade for device-id: %s",
+ oFsm.deviceID))
+ }
+ oFsm.commitImage = true
+ oFsm.mutexUpgradeParams.Unlock()
+ var pBaseFsm *fsm.FSM = nil
+ if oFsm.pAdaptFsm != nil {
+ pBaseFsm = oFsm.pAdaptFsm.pFsm
+ }
+ if pBaseFsm != nil {
+ if pBaseFsm.Is(upgradeStWaitForCommit) {
+ logger.Debugw(ctx, "OnuUpgradeFsm finish waiting for commit", log.Fields{"device-id": oFsm.deviceID})
+ _ = pBaseFsm.Event(upgradeEvCommitSw) //no need to call the FSM event in background here
+ }
+ return nil
+ }
+ logger.Errorw(ctx, "OnuUpgradeFsm abort: invalid FSM base pointer", log.Fields{
+ "device-id": oFsm.deviceID})
+ return fmt.Errorf(fmt.Sprintf("OnuUpgradeFsm abort: invalid FSM base pointer for device-id: %s", oFsm.deviceID))
+}
+
+//SetCommitmentParamsStart starts upgrade processing with immediate commitment
+// called from 'new' API Commit_onu_image
+func (oFsm *OnuUpgradeFsm) SetCommitmentParamsStart(ctx context.Context, aImageVersion string, aActiveImageID uint16) error {
+ oFsm.mutexUpgradeParams.Lock()
+ var pBaseFsm *fsm.FSM = nil
+ if oFsm.pAdaptFsm != nil {
+ pBaseFsm = oFsm.pAdaptFsm.pFsm
+ }
+ if pBaseFsm != nil && pBaseFsm.Is(upgradeStStarting) {
+ logger.Debugw(ctx, "OnuUpgradeFsm Parameter setting to start with commitment", log.Fields{
+ "device-id": oFsm.deviceID, "image-version": aImageVersion})
+ oFsm.useAPIVersion43 = true
+ oFsm.inactiveImageMeID = aActiveImageID //upgrade state machines inactive ImageId is the new active ImageId
+ oFsm.imageVersion = aImageVersion
+ oFsm.commitImage = true
+ oFsm.mutexUpgradeParams.Unlock()
+ //directly request the FSM to activate the image
+ _ = pBaseFsm.Event(upgradeEvCommitSw) //no need to call the FSM event in background here
+ return nil
+ }
+ oFsm.mutexUpgradeParams.Unlock()
+ logger.Errorw(ctx, "OnuUpgradeFsm abort: invalid FSM base pointer or state", log.Fields{
+ "device-id": oFsm.deviceID})
+ return fmt.Errorf(fmt.Sprintf("OnuUpgradeFsm abort: invalid FSM base pointer or state for device-id: %s", oFsm.deviceID))
+}
+
+//CancelProcessing ensures that suspended processing at waiting on some response is aborted and reset of FSM
+func (oFsm *OnuUpgradeFsm) CancelProcessing(ctx context.Context) {
+ //mutex protection is required for possible concurrent access to FSM members
+ //attention: for an unbuffered channel the sender is blocked until the value is received (processed)!
+ // accordingly the mutex must be released before sending to channel here (mutex acquired in receiver)
+ oFsm.mutexIsAwaitingAdapterDlResponse.RLock()
+ if oFsm.isWaitingForAdapterDlResponse {
+ oFsm.mutexIsAwaitingAdapterDlResponse.RUnlock()
+ //use channel to indicate that the download response waiting shall be aborted for this device (channel)
+ oFsm.chAdapterDlReady <- false
+ } else {
+ oFsm.mutexIsAwaitingAdapterDlResponse.RUnlock()
+ }
+ //chOnuDlReady is cleared as part of the FSM reset processing (from enterResetting())
+
+ // in any case (even if it might be automatically requested by above cancellation of waiting) ensure resetting the FSM
+ // specific here: If the FSM is in upgradeStWaitForCommit, it is left there for possibly later commit
+ // this possibly also refers later to (not yet existing) upgradeStWaitForActivate (with ctl API changes)
+ pAdaptFsm := oFsm.pAdaptFsm
+ if pAdaptFsm != nil {
+ // calling FSM events in background to avoid blocking of the caller
+ go func(aPAFsm *AdapterFsm) {
+ if aPAFsm.pFsm != nil {
+ if aPAFsm.pFsm.Is(upgradeStWaitEndDL) {
+ oFsm.chReceiveExpectedResponse <- false //which aborts the FSM (activate was not yet sent)
+ }
+ _ = aPAFsm.pFsm.Event(upgradeEvReset) //anyway and for all other states
+ } //else the FSM seems already to be in some released state
+ }(pAdaptFsm)
+ }
+}
+
func (oFsm *OnuUpgradeFsm) enterStarting(ctx context.Context, e *fsm.Event) {
logger.Debugw(ctx, "OnuUpgradeFsm start", log.Fields{"in state": e.FSM.Current(),
"device-id": oFsm.deviceID})
@@ -232,11 +446,26 @@
go oFsm.processOmciUpgradeMessages(ctx)
}
+//enterWaitingAdapterDL state can only be reached with useAPIVersion43
+func (oFsm *OnuUpgradeFsm) enterWaitingAdapterDL(ctx context.Context, e *fsm.Event) {
+ logger.Debugw(ctx, "OnuUpgradeFsm waiting for adapter download", log.Fields{"in state": e.FSM.Current(),
+ "device-id": oFsm.deviceID})
+ go oFsm.waitOnDownloadToAdapterReady(ctx, oFsm.chAdapterDlReady)
+ go oFsm.pFileManager.RequestDownloadReady(ctx, oFsm.imageIdentifier, oFsm.chAdapterDlReady)
+}
+
func (oFsm *OnuUpgradeFsm) enterPreparingDL(ctx context.Context, e *fsm.Event) {
logger.Debugw(ctx, "OnuUpgradeFsm prepare Download to Onu", log.Fields{"in state": e.FSM.Current(),
"device-id": oFsm.deviceID})
- fileLen, err := oFsm.pDownloadManager.getImageBufferLen(ctx, oFsm.pImageDsc.Name, oFsm.pImageDsc.LocalDir)
+ var fileLen int64
+ var err error
+ if oFsm.useAPIVersion43 {
+ //with the new API structure download to adapter is implicit and we have to wait until the image is available
+ fileLen, err = oFsm.pFileManager.GetImageBufferLen(ctx, oFsm.imageIdentifier)
+ } else {
+ fileLen, err = oFsm.pDownloadManager.getImageBufferLen(ctx, oFsm.pImageDsc.Name, oFsm.pImageDsc.LocalDir)
+ }
if err != nil || fileLen > int64(cMaxUint32) {
logger.Errorw(ctx, "OnuUpgradeFsm abort: problems getting image buffer length", log.Fields{
"device-id": oFsm.deviceID, "error": err, "length": fileLen})
@@ -248,8 +477,13 @@
return
}
+ //copy file content to buffer
oFsm.imageBuffer = make([]byte, fileLen)
- oFsm.imageBuffer, err = oFsm.pDownloadManager.getDownloadImageBuffer(ctx, oFsm.pImageDsc.Name, oFsm.pImageDsc.LocalDir)
+ if oFsm.useAPIVersion43 {
+ oFsm.imageBuffer, err = oFsm.pFileManager.GetDownloadImageBuffer(ctx, oFsm.imageIdentifier)
+ } else {
+ oFsm.imageBuffer, err = oFsm.pDownloadManager.getDownloadImageBuffer(ctx, oFsm.pImageDsc.Name, oFsm.pImageDsc.LocalDir)
+ }
if err != nil {
logger.Errorw(ctx, "OnuUpgradeFsm abort: can't get image buffer", log.Fields{
"device-id": oFsm.deviceID, "error": err})
@@ -263,14 +497,16 @@
oFsm.noOfSections = uint32(fileLen / cOmciDownloadSectionSize)
if fileLen%cOmciDownloadSectionSize > 0 {
- bufferPadding := make([]byte, cOmciDownloadSectionSize-uint32(fileLen%cOmciDownloadSectionSize))
+ bufferPadding := make([]byte, cOmciDownloadSectionSize-uint32((fileLen)%cOmciDownloadSectionSize))
//expand the imageBuffer to exactly fit multiples of cOmciDownloadSectionSize with padding
- oFsm.imageBuffer = append(oFsm.imageBuffer[:fileLen], bufferPadding...)
+ oFsm.imageBuffer = append(oFsm.imageBuffer[:(fileLen)], bufferPadding...)
oFsm.noOfSections++
}
oFsm.origImageLength = uint32(fileLen)
oFsm.imageLength = uint32(len(oFsm.imageBuffer))
+ go oFsm.waitOnDownloadToOnuReady(ctx, oFsm.chOnuDlReady) // start supervision of the complete download-to-ONU procedure
+
logger.Infow(ctx, "OnuUpgradeFsm starts with StartSwDl values", log.Fields{
"MeId": oFsm.inactiveImageMeID, "windowSizeLimit": oFsm.omciDownloadWindowSizeLimit,
"ImageSize": oFsm.imageLength, "original file size": fileLen})
@@ -457,9 +693,17 @@
}
if success {
//answer received with ready indication
- go func(a_pAFsm *AdapterFsm) {
- _ = a_pAFsm.pFsm.Event(upgradeEvRequestActivate)
- }(pBaseFsm)
+ if oFsm.activateImage {
+ //immediate activation requested
+ go func(a_pAFsm *AdapterFsm) {
+ _ = a_pAFsm.pFsm.Event(upgradeEvRequestActivate)
+ }(pBaseFsm)
+ } else {
+ //have to wait on explicit activation request
+ go func(a_pAFsm *AdapterFsm) {
+ _ = a_pAFsm.pFsm.Event(upgradeEvWaitForActivate)
+ }(pBaseFsm)
+ }
return
}
//timer was aborted
@@ -557,6 +801,16 @@
func (oFsm *OnuUpgradeFsm) enterResetting(ctx context.Context, e *fsm.Event) {
logger.Debugw(ctx, "OnuUpgradeFsm resetting", log.Fields{"device-id": oFsm.deviceID})
+ // in case the download-to-ONU timer is still running - cancel it
+ oFsm.mutexIsAwaitingOnuDlResponse.RLock()
+ if oFsm.isWaitingForOnuDlResponse {
+ oFsm.mutexIsAwaitingOnuDlResponse.RUnlock()
+ //use channel to indicate that the download response waiting shall be aborted for this device (channel)
+ oFsm.chOnuDlReady <- false
+ } else {
+ oFsm.mutexIsAwaitingOnuDlResponse.RUnlock()
+ }
+
pConfigupgradeStateAFsm := oFsm.pAdaptFsm
if pConfigupgradeStateAFsm != nil {
// abort running message processing
@@ -580,12 +834,7 @@
func (oFsm *OnuUpgradeFsm) enterDisabled(ctx context.Context, e *fsm.Event) {
logger.Debugw(ctx, "OnuUpgradeFsm enters disabled state", log.Fields{"device-id": oFsm.deviceID})
- //flush possible left-over channels
- select {
- case <-oFsm.chReceiveExpectedResponse:
- logger.Debug(ctx, "OnuUpgradeFsm chReceiveExpectedResponse flushed", log.Fields{"for device-id": oFsm.deviceID})
- default:
- }
+ // no need to flush possible channels here, Upgrade FSM will be completely removed, garbage collector should find its way
if oFsm.pDeviceHandler != nil {
//request removal of 'reference' in the Handler (completely clear the FSM and its data)
go oFsm.pDeviceHandler.removeOnuUpgradeFsm(ctx)
@@ -732,8 +981,13 @@
_ = oFsm.pAdaptFsm.pFsm.Event(upgradeEvAbort)
return
}
- oFsm.delayEndSwDl = true //ensure a delay for the EndSwDl message
- oFsm.imageCRC = crc32a.Checksum(oFsm.imageBuffer[:int(oFsm.origImageLength)]) //store internal for multiple usage
+ oFsm.delayEndSwDl = true //ensure a delay for the EndSwDl message
+ //CRC computation for all data bytes of the file
+ imageCRC := crc32a.Checksum(oFsm.imageBuffer[:int(oFsm.origImageLength)]) //store internal for multiple usage
+ //revert the retrieved CRC Byte Order (seems not to deliver NetworkByteOrder)
+ var byteSlice []byte = make([]byte, 4)
+ binary.LittleEndian.PutUint32(byteSlice, uint32(imageCRC))
+ oFsm.imageCRC = binary.BigEndian.Uint32(byteSlice)
_ = oFsm.pAdaptFsm.pFsm.Event(upgradeEvEndSwDownload)
return
}
@@ -792,6 +1046,14 @@
}
if msgObj.EntityInstance == oFsm.inactiveImageMeID {
logger.Debugw(ctx, "Expected EndSwDlResponse received", log.Fields{"device-id": oFsm.deviceID})
+ oFsm.mutexIsAwaitingOnuDlResponse.RLock()
+ if oFsm.isWaitingForOnuDlResponse {
+ oFsm.mutexIsAwaitingOnuDlResponse.RUnlock()
+ //use channel to indicate that the download to ONU was successful
+ oFsm.chOnuDlReady <- true
+ } else {
+ oFsm.mutexIsAwaitingOnuDlResponse.RUnlock()
+ }
oFsm.chReceiveExpectedResponse <- true //let the FSM proceed from the waitState
return
}
@@ -922,12 +1184,27 @@
logger.Debugw(ctx, "OnuUpgradeFsm - GetResponse Data for SoftwareImage",
log.Fields{"device-id": oFsm.deviceID, "entityID": msgObj.EntityInstance,
"version": imageVersion, "isActive": imageIsActive, "isCommitted": imageIsCommitted})
-
- //a check on the delivered image version is not done, the ONU delivered version might be different from what might have been
- // indicated in the download image version string (version must be part of the image content itself)
- // so checking that might be quite unreliable
if msgObj.EntityInstance == oFsm.inactiveImageMeID && imageIsActive == swIsActive &&
imageIsCommitted == swIsCommitted {
+ //a check on the delivered image version is not done, the ONU delivered version might be different from what might have been
+ // indicated in the download image version string (version must be part of the image content itself)
+ // so checking that might be quite unreliable
+ //but with new API this was changed, assumption is that omci image version is known at download request and exactly that is used
+ // in all the API references, so it can and should be checked here now
+ if oFsm.useAPIVersion43 {
+ if imageVersion != oFsm.imageVersion {
+ //new active version indicated on OMCI from ONU is not the expected version
+ logger.Errorw(ctx, "OnuUpgradeFsm SwImage GetResponse indications not matching requested upgrade",
+ log.Fields{"device-id": oFsm.deviceID, "ResponseMeId": msgObj.EntityInstance,
+ "onu-version": imageVersion, "expected-version": oFsm.imageVersion})
+ // TODO!!!: error treatment?
+ //TODO!!!: possibly send event information for aborted upgrade (aborted by omci processing)??
+ _ = oFsm.pAdaptFsm.pFsm.Event(upgradeEvAbort)
+ return
+ }
+ logger.Debugw(ctx, "OnuUpgradeFsm - expected ONU image version indicated by the ONU",
+ log.Fields{"device-id": oFsm.deviceID})
+ }
logger.Infow(ctx, "requested SW image committed, releasing OnuUpgrade", log.Fields{"device-id": oFsm.deviceID})
//releasing the upgrade FSM
_ = oFsm.pAdaptFsm.pFsm.Event(upgradeEvReset)
@@ -949,23 +1226,106 @@
}
}
-/*
-func (oFsm *OnuUpgradeFsm) waitforOmciResponse(ctx context.Context) error {
+//waitOnDownloadToAdapterReady state can only be reached with useAPIVersion43 (usage of pFileManager)
+func (oFsm *OnuUpgradeFsm) waitOnDownloadToAdapterReady(ctx context.Context, aWaitChannel chan bool) {
+ downloadToAdapterTimeout := oFsm.pFileManager.GetDownloadTimeout(ctx)
+ oFsm.mutexIsAwaitingAdapterDlResponse.Lock()
+ oFsm.isWaitingForAdapterDlResponse = true
+ oFsm.mutexIsAwaitingAdapterDlResponse.Unlock()
select {
// maybe be also some outside cancel (but no context modeled for the moment ...)
// case <-ctx.Done():
- // logger.Infow(ctx,"LockState-bridge-init message reception canceled", log.Fields{"for device-id": oFsm.deviceID})
- case <-time.After(30 * time.Second): //AS FOR THE OTHER OMCI FSM's
- logger.Warnw(ctx, "OnuUpgradeFsm multi entity timeout", log.Fields{"for device-id": oFsm.deviceID})
- return fmt.Errorf("OnuUpgradeFsm multi entity timeout %s", oFsm.deviceID)
- case success := <-oFsm.omciMIdsResponseReceived:
- if success {
- logger.Debug(ctx, "OnuUpgradeFsm multi entity response received")
- return nil
+ // logger.Infow("OnuUpgradeFsm-waitOnDownloadToAdapterReady canceled", log.Fields{"for device-id": oFsm.deviceID})
+ case <-time.After(downloadToAdapterTimeout): //10s should be enough for downloading some image to the adapter
+ logger.Warnw(ctx, "OnuUpgradeFsm Waiting-adapter-download timeout", log.Fields{
+ "for device-id": oFsm.deviceID, "image-id": oFsm.imageIdentifier, "timeout": downloadToAdapterTimeout})
+ oFsm.pFileManager.RemoveReadyRequest(ctx, oFsm.imageIdentifier, aWaitChannel)
+ oFsm.mutexIsAwaitingAdapterDlResponse.Lock()
+ oFsm.isWaitingForAdapterDlResponse = false
+ oFsm.mutexIsAwaitingAdapterDlResponse.Unlock()
+ //the upgrade process has to be aborted
+ pUpgradeFsm := oFsm.pAdaptFsm
+ if pUpgradeFsm != nil {
+ _ = pUpgradeFsm.pFsm.Event(upgradeEvReset)
+ } else {
+ logger.Errorw(ctx, "pUpgradeFsm is nil", log.Fields{"device-id": oFsm.deviceID})
}
- // should not happen so far
- logger.Warnw(ctx, "OnuUpgradeFsm multi entity response error", log.Fields{"for device-id": oFsm.deviceID})
- return fmt.Errorf("OnuUpgradeFsm multi entity responseError %s", oFsm.deviceID)
+ return
+
+ case success := <-aWaitChannel:
+ if success {
+ logger.Debugw(ctx, "OnuUpgradeFsm image-downloaded received", log.Fields{"device-id": oFsm.deviceID})
+ oFsm.mutexIsAwaitingAdapterDlResponse.Lock()
+ oFsm.isWaitingForAdapterDlResponse = false
+ oFsm.mutexIsAwaitingAdapterDlResponse.Unlock()
+ //let the upgrade process proceed
+ pUpgradeFsm := oFsm.pAdaptFsm
+ if pUpgradeFsm != nil {
+ _ = pUpgradeFsm.pFsm.Event(upgradeEvPrepareSwDownload)
+ } else {
+ logger.Errorw(ctx, "pUpgradeFsm is nil", log.Fields{"device-id": oFsm.deviceID})
+ }
+ return
+ }
+ // waiting was aborted (probably on external request)
+ logger.Debugw(ctx, "OnuUpgradeFsm Waiting-adapter-download aborted", log.Fields{"device-id": oFsm.deviceID})
+ oFsm.pFileManager.RemoveReadyRequest(ctx, oFsm.imageIdentifier, aWaitChannel)
+ oFsm.mutexIsAwaitingAdapterDlResponse.Lock()
+ oFsm.isWaitingForAdapterDlResponse = false
+ oFsm.mutexIsAwaitingAdapterDlResponse.Unlock()
+ //the upgrade process has to be aborted
+ pUpgradeFsm := oFsm.pAdaptFsm
+ if pUpgradeFsm != nil {
+ _ = pUpgradeFsm.pFsm.Event(upgradeEvAbort)
+ } else {
+ logger.Errorw(ctx, "pUpgradeFsm is nil", log.Fields{"device-id": oFsm.deviceID})
+ }
+ return
}
}
-*/
+
+//waitOnDownloadToOnuReady state can only be reached with useAPIVersion43 (usage of pFileManager)
+func (oFsm *OnuUpgradeFsm) waitOnDownloadToOnuReady(ctx context.Context, aWaitChannel chan bool) {
+ downloadToOnuTimeout := time.Duration(1+(oFsm.imageLength/0x400000)) * oFsm.downloadToOnuTimeout4MB
+ logger.Debugw(ctx, "OnuUpgradeFsm start download-to-ONU timer", log.Fields{"device-id": oFsm.deviceID,
+ "duration": downloadToOnuTimeout})
+ oFsm.mutexIsAwaitingOnuDlResponse.Lock()
+ oFsm.isWaitingForOnuDlResponse = true
+ oFsm.mutexIsAwaitingOnuDlResponse.Unlock()
+ select {
+ // maybe be also some outside cancel (but no context modeled for the moment ...)
+ // case <-ctx.Done():
+ // logger.Infow("OnuUpgradeFsm-waitOnDownloadToOnuReady canceled", log.Fields{"for device-id": oFsm.deviceID})
+ case <-time.After(downloadToOnuTimeout): //using an image-size depending timout (in minutes)
+ logger.Warnw(ctx, "OnuUpgradeFsm Waiting-ONU-download timeout", log.Fields{
+ "for device-id": oFsm.deviceID, "image-id": oFsm.imageIdentifier, "timeout": downloadToOnuTimeout})
+ oFsm.mutexIsAwaitingOnuDlResponse.Lock()
+ oFsm.isWaitingForOnuDlResponse = false
+ oFsm.mutexIsAwaitingOnuDlResponse.Unlock()
+ //the upgrade process has to be aborted
+ pUpgradeFsm := oFsm.pAdaptFsm
+ if pUpgradeFsm != nil {
+ _ = pUpgradeFsm.pFsm.Event(upgradeEvAbort)
+ } else {
+ logger.Errorw(ctx, "pUpgradeFsm is nil", log.Fields{"device-id": oFsm.deviceID})
+ }
+ return
+
+ case success := <-aWaitChannel:
+ if success {
+ logger.Debugw(ctx, "OnuUpgradeFsm image-downloaded on ONU received", log.Fields{"device-id": oFsm.deviceID})
+ oFsm.mutexIsAwaitingOnuDlResponse.Lock()
+ oFsm.isWaitingForOnuDlResponse = false
+ oFsm.mutexIsAwaitingOnuDlResponse.Unlock()
+ //all fine, let the FSM proceed like defined from the sender of this event
+ return
+ }
+ // waiting was aborted (assumed here to be caused by
+ // error detection or cancel at download after upgrade FSM reset/abort)
+ logger.Debugw(ctx, "OnuUpgradeFsm Waiting-ONU-download aborted", log.Fields{"device-id": oFsm.deviceID})
+ oFsm.mutexIsAwaitingOnuDlResponse.Lock()
+ oFsm.isWaitingForOnuDlResponse = false
+ oFsm.mutexIsAwaitingOnuDlResponse.Unlock()
+ return
+ }
+}
diff --git a/internal/pkg/onuadaptercore/openonu.go b/internal/pkg/onuadaptercore/openonu.go
index d1d6b09..c61ce5f 100644
--- a/internal/pkg/onuadaptercore/openonu.go
+++ b/internal/pkg/onuadaptercore/openonu.go
@@ -68,10 +68,12 @@
maxTimeoutInterAdapterComm time.Duration
maxTimeoutReconciling time.Duration
pDownloadManager *adapterDownloadManager
+ pFileManager *fileDownloadManager //let coexist 'old and new' DownloadManager as long as 'old' does not get obsolete
metricsEnabled bool
mibAuditInterval time.Duration
omciTimeout int // in seconds
alarmAuditInterval time.Duration
+ dlToOnuTimeout4M time.Duration
}
//NewOpenONUAC returns a new instance of OpenONU_AC
@@ -107,6 +109,7 @@
// since consumers of OMCI timeout value everywhere in code is in "int seconds", do this useful conversion
openOnuAc.omciTimeout = int(cfg.OmciTimeout.Seconds())
openOnuAc.alarmAuditInterval = cfg.AlarmAuditInterval
+ openOnuAc.dlToOnuTimeout4M = cfg.DownloadToOnuTimeout4MB
openOnuAc.pSupportedFsms = &OmciDeviceFsms{
"mib-synchronizer": {
@@ -126,6 +129,8 @@
}
openOnuAc.pDownloadManager = newAdapterDownloadManager(ctx)
+ openOnuAc.pFileManager = newFileDownloadManager(ctx)
+ openOnuAc.pFileManager.SetDownloadTimeout(ctx, cfg.DownloadToAdapterTimeout)
return &openOnuAc
}
@@ -606,11 +611,87 @@
}
//if update >= 4.3.0
+// Note: already with the implementation of the 'old' download interface problems were detected when the argument name used here is not the same
+// as defined in the adapter interface file. That sounds strange and the effects were strange as well.
+// The reason for that was never finally investigated.
+// To be on the safe side argument names are left here always as defined in iAdapter.go .
// Download_onu_image downloads (and optionally activates and commits) the indicated ONU image to the requested ONU(s)
// if the image is not yet present on the adapter it has to be automatically downloaded
func (oo *OpenONUAC) Download_onu_image(ctx context.Context, request *voltha.DeviceImageDownloadRequest) (*voltha.DeviceImageResponse, error) {
- return nil, errors.New("unImplemented")
+ if request != nil && len((*request).DeviceId) > 0 && (*request).Image.Version != "" {
+ loResponse := voltha.DeviceImageResponse{}
+ imageIdentifier := (*request).Image.Version
+ //inform the deviceHandler about (possibly new) requested ONU download requests
+ firstDevice := true
+ var vendorID string
+ for _, pCommonID := range (*request).DeviceId {
+ loDeviceID := (*pCommonID).Id
+ onuVolthaDevice, err := oo.coreProxy.GetDevice(log.WithSpanFromContext(context.TODO(), ctx),
+ loDeviceID, loDeviceID)
+ if err != nil || onuVolthaDevice == nil {
+ logger.Warnw(ctx, "Failed to fetch Onu device for image download",
+ log.Fields{"device-id": loDeviceID, "err": err})
+ continue //try the work with next deviceId
+ }
+ if firstDevice {
+ //start/verify download of the image to the adapter based on first found device only
+ // use the OnuVendor identification from first given device
+ firstDevice = false
+ vendorID = onuVolthaDevice.VendorId
+ imageIdentifier = vendorID + imageIdentifier //head on vendor ID of the ONU
+ logger.Debugw(ctx, "download request for file", log.Fields{"image-id": imageIdentifier})
+
+ if !oo.pFileManager.ImageExists(ctx, imageIdentifier) {
+ logger.Debugw(ctx, "start image download", log.Fields{"image-description": request})
+ // Download_image is not supposed to be blocking, anyway let's call the DownloadManager still synchronously to detect 'fast' problems
+ // the download itself is later done in background
+ if err := oo.pFileManager.StartDownload(ctx, imageIdentifier, (*request).Image.Url); err != nil {
+ return nil, err
+ }
+ }
+ // image already exists
+ logger.Debugw(ctx, "image already downloaded", log.Fields{"image-description": imageIdentifier})
+ } else {
+ //for all following devices verify the matching vendorID
+ if onuVolthaDevice.VendorId != vendorID {
+ logger.Warnw(ctx, "onu vendor id does not match image vendor id, device ignored",
+ log.Fields{"onu-vendor-id": onuVolthaDevice.VendorId, "image-vendor-id": vendorID})
+ continue //try the work with next deviceId
+ }
+ }
+ // start the ONU download activity for each possible device
+ // assumption here is, that the concerned device was already created (automatic start after device creation not supported)
+ if handler := oo.getDeviceHandler(ctx, loDeviceID, false); handler != nil {
+ logger.Debugw(ctx, "image download on omci requested", log.Fields{
+ "image-id": imageIdentifier, "device-id": loDeviceID})
+ //onu upgrade handling called in background without immediate error evaluation here
+ // as the processing can be done for multiple ONU's and an error on one ONU should not stop processing for others
+ // state/progress/success of the request has to be verified using the Get_onu_image_status() API
+ go handler.onuSwUpgradeAfterDownload(ctx, request, oo.pFileManager, imageIdentifier)
+ loDeviceImageState := voltha.DeviceImageState{}
+ loDeviceImageState.DeviceId = loDeviceID
+ loDeviceImageState.ImageState.Version = (*request).Image.Version
+ loDeviceImageState.ImageState.DownloadState = voltha.ImageState_DOWNLOAD_STARTED
+ loDeviceImageState.ImageState.Reason = voltha.ImageState_NO_ERROR
+ loDeviceImageState.ImageState.ImageState = voltha.ImageState_IMAGE_UNKNOWN
+ loResponse.DeviceImageStates = append(loResponse.DeviceImageStates, &loDeviceImageState)
+ } else {
+ //cannot start ONU download for requested device
+ logger.Warnw(ctx, "no handler found for image activation", log.Fields{"device-id": loDeviceID})
+ loDeviceImageState := voltha.DeviceImageState{}
+ loDeviceImageState.DeviceId = loDeviceID
+ loDeviceImageState.ImageState.Version = (*request).Image.Version
+ loDeviceImageState.ImageState.DownloadState = voltha.ImageState_DOWNLOAD_FAILED
+ loDeviceImageState.ImageState.Reason = voltha.ImageState_UNKNOWN_ERROR
+ loDeviceImageState.ImageState.ImageState = voltha.ImageState_IMAGE_UNKNOWN
+ loResponse.DeviceImageStates = append(loResponse.DeviceImageStates, &loDeviceImageState)
+ }
+ }
+ pImageResp := &loResponse
+ return pImageResp, nil
+ }
+ return nil, errors.New("invalid image download parameters")
}
// Get_onu_image_status delivers the adapter-related information about the download/activation/commitment
@@ -630,15 +711,93 @@
}
// Activate_onu_image initiates the activation of the image for the requested ONU(s)
-// precondition: image downloaded and not yet activated
+// precondition: image downloaded and not yet activated or image refers to current inactive image
func (oo *OpenONUAC) Activate_onu_image(ctx context.Context, in *voltha.DeviceImageRequest) (*voltha.DeviceImageResponse, error) {
- return nil, errors.New("unImplemented")
+ if in != nil && len((*in).DeviceId) > 0 && (*in).Version != "" {
+ loResponse := voltha.DeviceImageResponse{}
+ imageIdentifier := (*in).Version
+ //let the deviceHandler find the adequate way of requesting the image activation
+ for _, pCommonID := range (*in).DeviceId {
+ loDeviceID := (*pCommonID).Id
+ //compared to download procedure the vendorID (from device) is secondary here
+ // and only needed in case the upgrade process is based on some ongoing download process (and can be retrieved in deviceHandler if needed)
+ // start image activation activity for each possible device
+ // assumption here is, that the concerned device was already created (automatic start after device creation not supported)
+ if handler := oo.getDeviceHandler(ctx, loDeviceID, false); handler != nil {
+ logger.Debugw(ctx, "onu image activation requested", log.Fields{
+ "image-id": imageIdentifier, "device-id": loDeviceID})
+ //onu activation handling called in background without immediate error evaluation here
+ // as the processing can be done for multiple ONU's and an error on one ONU should not stop processing for others
+ // state/progress/success of the request has to be verified using the Get_onu_image_status() API
+ go handler.onuSwActivateRequest(ctx, imageIdentifier, (*in).CommitOnSuccess)
+ loDeviceImageState := voltha.DeviceImageState{}
+ loDeviceImageState.DeviceId = loDeviceID
+ loDeviceImageState.ImageState.Version = imageIdentifier
+ loDeviceImageState.ImageState.DownloadState = voltha.ImageState_DOWNLOAD_UNKNOWN
+ loDeviceImageState.ImageState.Reason = voltha.ImageState_NO_ERROR
+ loDeviceImageState.ImageState.ImageState = voltha.ImageState_IMAGE_ACTIVATING
+ loResponse.DeviceImageStates = append(loResponse.DeviceImageStates, &loDeviceImageState)
+ } else {
+ //cannot start SW activation for requested device
+ logger.Warnw(ctx, "no handler found for image activation", log.Fields{"device-id": loDeviceID})
+ loDeviceImageState := voltha.DeviceImageState{}
+ loDeviceImageState.DeviceId = loDeviceID
+ loDeviceImageState.ImageState.Version = imageIdentifier
+ loDeviceImageState.ImageState.DownloadState = voltha.ImageState_DOWNLOAD_UNKNOWN
+ loDeviceImageState.ImageState.Reason = voltha.ImageState_UNKNOWN_ERROR
+ loDeviceImageState.ImageState.ImageState = voltha.ImageState_IMAGE_ACTIVATION_ABORTED
+ loResponse.DeviceImageStates = append(loResponse.DeviceImageStates, &loDeviceImageState)
+ }
+ }
+ pImageResp := &loResponse
+ return pImageResp, nil
+ }
+ return nil, errors.New("invalid image activation parameters")
}
// Commit_onu_image enforces the commitment of the image for the requested ONU(s)
// precondition: image activated and not yet committed
func (oo *OpenONUAC) Commit_onu_image(ctx context.Context, in *voltha.DeviceImageRequest) (*voltha.DeviceImageResponse, error) {
- return nil, errors.New("unImplemented")
+ if in != nil && len((*in).DeviceId) > 0 && (*in).Version != "" {
+ loResponse := voltha.DeviceImageResponse{}
+ imageIdentifier := (*in).Version
+ //let the deviceHandler find the adequate way of requesting the image activation
+ for _, pCommonID := range (*in).DeviceId {
+ loDeviceID := (*pCommonID).Id
+ //compared to download procedure the vendorID (from device) is secondary here
+ // and only needed in case the upgrade process is based on some ongoing download process (and can be retrieved in deviceHandler if needed)
+ // start image activation activity for each possible device
+ // assumption here is, that the concerned device was already created (automatic start after device creation not supported)
+ if handler := oo.getDeviceHandler(ctx, loDeviceID, false); handler != nil {
+ logger.Debugw(ctx, "onu image commitment requested", log.Fields{
+ "image-id": imageIdentifier, "device-id": loDeviceID})
+ //onu commitment handling called in background without immediate error evaluation here
+ // as the processing can be done for multiple ONU's and an error on one ONU should not stop processing for others
+ // state/progress/success of the request has to be verified using the Get_onu_image_status() API
+ go handler.onuSwCommitRequest(ctx, imageIdentifier)
+ loDeviceImageState := voltha.DeviceImageState{}
+ loDeviceImageState.DeviceId = loDeviceID
+ loDeviceImageState.ImageState.Version = imageIdentifier
+ loDeviceImageState.ImageState.DownloadState = voltha.ImageState_DOWNLOAD_UNKNOWN
+ loDeviceImageState.ImageState.Reason = voltha.ImageState_NO_ERROR
+ loDeviceImageState.ImageState.ImageState = voltha.ImageState_IMAGE_COMMITTING
+ loResponse.DeviceImageStates = append(loResponse.DeviceImageStates, &loDeviceImageState)
+ } else {
+ //cannot start SW commitment for requested device
+ logger.Warnw(ctx, "no handler found for image commitment", log.Fields{"device-id": loDeviceID})
+ loDeviceImageState := voltha.DeviceImageState{}
+ loDeviceImageState.DeviceId = loDeviceID
+ loDeviceImageState.ImageState.Version = imageIdentifier
+ loDeviceImageState.ImageState.DownloadState = voltha.ImageState_DOWNLOAD_UNKNOWN
+ loDeviceImageState.ImageState.Reason = voltha.ImageState_UNKNOWN_ERROR
+ loDeviceImageState.ImageState.ImageState = voltha.ImageState_IMAGE_COMMIT_ABORTED
+ loResponse.DeviceImageStates = append(loResponse.DeviceImageStates, &loDeviceImageState)
+ }
+ }
+ pImageResp := &loResponse
+ return pImageResp, nil
+ }
+ return nil, errors.New("invalid image commitment parameters")
}
// Adapter interface required methods ################ end #########