From a8e0758d1201a1610674f5fa0c251b41b0ab7897 Mon Sep 17 00:00:00 2001 From: Mengling Ding <71745861+Meng-20@users.noreply.github.com> Date: Tue, 10 Sep 2024 15:59:56 -0600 Subject: [PATCH 01/12] feat: add a help page (#28) * feat: add icon and background image * feat: delete the unused image * feat: help page * feat: add header comment and renumber the variant ids in help page * feat: point troubleshooting to the corresponding wiki page * feat: add period to the sentence --- .../icons/Icons-Questions--Web-Green.png | Bin 0 -> 3274 bytes webui/src/components/Help/Help.vue | 78 ++++++++++++++++++ webui/src/components/SideBar.vue | 6 ++ webui/src/router/index.ts | 5 ++ webui/src/views/Help.vue | 8 ++ 5 files changed, 97 insertions(+) create mode 100644 webui/src/assets/icons/Icons-Questions--Web-Green.png create mode 100644 webui/src/components/Help/Help.vue create mode 100644 webui/src/views/Help.vue diff --git a/webui/src/assets/icons/Icons-Questions--Web-Green.png b/webui/src/assets/icons/Icons-Questions--Web-Green.png new file mode 100644 index 0000000000000000000000000000000000000000..6c703670904b8b85eb0a5899c8cdaab7d9f4b173 GIT binary patch literal 3274 zcmbtWhc_FH7AI7w%@-m@ZJGwDwpt^0sjXI1QVpe{F}@&1QPgPcty<%KY7{Y2t-V62 z)uLw15~~$adsNl)o%8;Kch9-^cg{Wco;%LD_xxfl%?&v(LM}2eFmM{((YHEB%D>9W za&BYA73k+6)nH)Y@G#QXu?d{s z$oqy9HspH>iJNX%kuX4aBz`PsQZ{07g`5g0>5D(KS z{;2x)0EyD9PX2w;2K}o)fc*)kBy4nUcT)Ad{QoK8VUP7eUES?rCoBIPLuhN{qxbmT zsopiMvtM{K1xa|+-i#7IXju5vD|#g|G0t4GeIP)p1yq|v_7NNdf<~l=u@yFc0jyjR z#ftLV2G*eQ>m!q>ILdM}?`({n9{MsXR-m!;nS7mr8@oMkM7L~t`LUY2=C$M?8~LvE z*j~+>&P4YI%mQlFYIaU51Xg-X2xmkz&xD+y7s>e>fvm^_0n8Z62R=w4)vSCy0V+-REdWm) zPj)!g-Dmn}hl4+*4|Wf=9*vzh^B0#fL1x#S7^>txB7?Nb;#QaEcaj3IuhzcF`8?Ml zeQTa0;uWVw)D7XIw?#MG-8JJW!T$SWR`ZX(EX9y43sQ3=NR<*}`(zye+ev-1kD~Hi2dvQe=L)Y1fNzpD_DMc+eQ8D*C`%=`Q4j;2X zgJ7l(CoL{AaTP@vsP5Hl@-`G{>9Uzg;JM0(vxPvC5~*>Dz+q*Z&6UbObX8FrSe=lh zx@Dl(4TPNfOx#8nym|^d4 z)T9K2fjkwCbSZHl!VfLOoB|K&-QVbNt%$@Tr$Ia~<%fYa)Fu zI-V8;_OHg*d#`@7#PNMUq#igDaL@xh|EWAfV}F8ZBv9^Xfz({8Wd z6qG>WCcPxCTfeS%fuGKqzG)YF+A{0%4OHy;;b3f)^|RicyQ(w(dqa%hs@IQqb*`9= zrE~W6&0lz8XW}60HYZ79ZP9CfuEDI=3A+T$f9SqlWn#1M)q@A&zocVfPaP zKu5O8lJJeTNKip*X))bJx5aoRuzr#a&mvIB_DmD~y%N*}DfAWtH*KnXIjQFK3{5f4 zKZah5s1)J2zrDrNaZ;@@1pv?pGL5HJH8p-r>mEIyTCr_`H5ZucB0D1S9n>_^-@Sj{ ztEHuWu9b7pMz>wCTem!Xe2x!3 zxYPuHu*v5}F@*FfCe`!tY+wx=^W!dOZSLq;+AO|3Fhn?>Y%=hx&XrFLmKj8P7rAzv z#qs!v(j>7Pv#U=8a0@uh=0`M4v(roR*J*@0eROBeh%X64-Qk58-2w0BjR{+*=n&kB zpc=3Z9}rPy?KQ?y!HCjz1t=3sgYn~JK4QzopWtelAbQ>VXC^@32Jd(gju4-9iM2Gw zNWHFTU}Sr6bpeJ^k(G@X?152+U(aAN6||9(kn~tXP<7EK)BslB5TaA)Q6Q zfcraN6?IBFPd4uS8OdQxu99Gg?7BtDzgrp#LMl2|SQu^_>f{YTGdqhuq!p@YT*C#4 zXxbmj8|KB5WmPk8$V9l}#aGxDhdR6Q{iB+F>*4ox^~Lq|(W^|CdqUSz#5Jbw zRVOn&0;8>xh8k9LjqPyxB?7U1yK^O9NIj)UcFYH~eLIs%WQzhC|GSQ|QXdYzvzSuCGMWZ{TaAj<2^~B3c0bK(a z1+Cq1ga!Y!eH$`yXC32%Y^BJ^ii*B$46YVomc=mG$82EAW{n>b4%6zgg~aCEgr!K@ zPH(P<@58iPkf`~A&NZ9C`QGtJ{I+v?$(jpUs^u_EB|BrGHEP-WUaDA?`v>+%)kkGP z{;$Y!yKQzx)p{s|j@wC~g`UMwX_XYX)1}&a#C7*hd8|?7jbji-9o0^Iezk5*q`7?B=FGfrE zS5`MUc%a!UG1=Q^$8n0yG~F4?lxXWxu|vsg_ykqNiVpdLDZNvXwI|&bCowLayO7Hp z30RC4(dQNQSv8gU0A^E>IAoraFOYhC=r}35v#3@-OmfY@F_{b1(R#oAjX77Am4J0YJMT#|gy^M}u)TsI5N^CF(`XTX`_pd{T;lK8po|`@&G#MJW zF1?}LW9xSv4jsGmE6$@((m}x9`@) z+=0@mydSD4tq{??xZI^TrB!1s645#qAKxo0_9vGkX14qLczzI(8v#^m!xU4UgMpEx=7SPhw8q?TFh^#()UfcH{HKtTR$GY(B3`(d-{8^Z7$TV-K7Qk#$Q`ke=q&w4|LDZ;H;fWIab)+%>@I7!N#%%CY6yZmVCC@oott-3e`Pz-w z1-e)=HV0sZDOAfA6n?&GCK)!D8T|S`4hxk$$Z}`?X>{9Mze*SV{J+UQ1wjA+ literal 0 HcmV?d00001 diff --git a/webui/src/components/Help/Help.vue b/webui/src/components/Help/Help.vue new file mode 100644 index 0000000..d9675ac --- /dev/null +++ b/webui/src/components/Help/Help.vue @@ -0,0 +1,78 @@ + + + + + + diff --git a/webui/src/components/SideBar.vue b/webui/src/components/SideBar.vue index eba643d..7e905fc 100644 --- a/webui/src/components/SideBar.vue +++ b/webui/src/components/SideBar.vue @@ -21,6 +21,12 @@ title="CXL-Hosts" value="CXL-Hosts" > + + + + \ No newline at end of file From 805cbb650564929c53013b047ed65b972828cfa2 Mon Sep 17 00:00:00 2001 From: Scott Howe Date: Wed, 18 Sep 2024 08:51:37 -0600 Subject: [PATCH 02/12] ci: implement a new github action utilizing the semantic-release tool (#30) This allows cfm releases to be generated automatically via github's ci\cd tools. While this new action can only be started manually (via github webui), the rest of the release process is now automated. --- .github/workflows/publish.yml | 70 ------------- .github/workflows/release-and-publish.yml | 109 ++++++++++++++++++++ .github/workflows/release.yml | 26 ----- .releaserc.yml | 119 ++++++++++++++++++++++ 4 files changed, 228 insertions(+), 96 deletions(-) delete mode 100644 .github/workflows/publish.yml create mode 100644 .github/workflows/release-and-publish.yml delete mode 100644 .github/workflows/release.yml create mode 100644 .releaserc.yml diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml deleted file mode 100644 index e295ab4..0000000 --- a/.github/workflows/publish.yml +++ /dev/null @@ -1,70 +0,0 @@ -name: Publish a Docker Image - -on: - workflow_run: - workflows: ["Release"] - types: [completed] - -env: - REGISTRY: ghcr.io - IMAGE_NAME: ${{ github.repository }} - -jobs: - build-and-push-image: - runs-on: ubuntu-22.04 - permissions: - contents: read - packages: write - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Fetch latest release version - id: fetch-latest-release - run: | - latest_release=$(curl -Ls \ - -H "Accept: application/vnd.github+json" \ - -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \ - -H "X-GitHub-Api-Version: 2022-11-28" \ - $GITHUB_API_URL/repos/$GITHUB_REPOSITORY/releases/latest | jq --raw-output '.tag_name') - echo "latest_release is $latest_release" - echo "latest-release=$latest_release" >> $GITHUB_OUTPUT - - - name: Lowercase - id: imagelc - uses: ASzc/change-string-case-action@v5 - with: - string: ${{ github.repository }} - - - name: Output - run: | - echo "released version is ${{ steps.fetch-latest-release.outputs.latest-release }}" - echo "image name is ${{ steps.imagelc.outputs.lowercase }}" - - - name: Log in to the Container registry - uses: docker/login-action@v2 - with: - registry: ${{ env.REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Extract metadata (tags, labels) for Docker - id: meta - uses: docker/metadata-action@v4 - with: - images: ${{ env.REGISTRY }}/${{ steps.imagelc.outputs.lowercase }} - - - name: Build and Push Docker Image - id: docker_build - uses: docker/build-push-action@v4 - with: - context: . - push: true - tags: ${{ env.REGISTRY }}/${{ steps.imagelc.outputs.lowercase }}:${{ steps.fetch-latest-release.outputs.latest-release }} - labels: ${{ steps.meta.outputs.labels }} - file: docker/Dockerfile - - - name: Inspect - run: | - docker image inspect ${{ env.REGISTRY }}/${{ steps.imagelc.outputs.lowercase }}:${{ steps.fetch-latest-release.outputs.latest-release }} diff --git a/.github/workflows/release-and-publish.yml b/.github/workflows/release-and-publish.yml new file mode 100644 index 0000000..977f284 --- /dev/null +++ b/.github/workflows/release-and-publish.yml @@ -0,0 +1,109 @@ +name: Release and Publish + +on: + workflow_dispatch: + +env: + LAST_VERSION: + NEXT_VERSION: + PUBLISH_IMAGE: + IMAGE_REGISTRY: ghcr.io + IMAGE_NAME: ghcr.io/${{ github.repository }} + +jobs: + release-and-publish: + permissions: + contents: write # to be able to publish a GitHub release + packages: write # to be able to upload a registry image + runs-on: ubuntu-22.04 + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup npm\npx + uses: actions/setup-node@v4 + with: + node-version: 20 + + - name: Install semantic-release and dependencies + run: npm install semantic-release@v24 @semantic-release/exec@v6 -D conventional-changelog-conventionalcommits@v8 + + # Need the dry-run to get the next version # BEFORE running the formal semantic release + - name: Run Semantic Release (dry-run) + run: npx semantic-release --dry-run + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Retrieve Semantic Release Dry-Run Output + run: | + echo 'LAST_VERSION = "${{ env.LAST_VERSION }}"' + echo 'NEXT_VERSION = "${{ env.NEXT_VERSION }}"' + if [ "${{ env.LAST_VERSION }}" != "${{ env.NEXT_VERSION }}" ]; then + echo "PUBLISH_IMAGE=true" >> $GITHUB_ENV + echo 'PUBLISH_IMAGE = "true"' + else + echo "PUBLISH_IMAGE=false" >> $GITHUB_ENV + echo 'PUBLISH_IMAGE = "false"' + fi + + # Update source code files where version is currently hardcoded BEFORE the real semantic-release is executed. + # This allows the release assets to be created using correctly versioned source code files. + # This source code version update is also leveraged during docker image creation. + - name: Update Local VERSION file (Conditional) + if: ${{ env.PUBLISH_IMAGE == 'true' }} + run: | + sed -i 's/1\.x\.x/${{ env.NEXT_VERSION }}/g' ./cmd/cfm-cli/cmd/root.go + sed -i 's/1\.x\.x/${{ env.NEXT_VERSION }}/g' ./cmd/cfm-service/main.go + sed -i 's/1\.x\.x/${{ env.NEXT_VERSION }}/g' ./cmd/cxl-host/service/parameters.go + sed -i 's/1\.x\.x/${{ env.NEXT_VERSION }}/g' ./webui/package.json + sed -i 's/1\.x\.x/${{ env.NEXT_VERSION }}/g' ./webui/package-lock.json + + # Test build to just make sure that it runs. + #TODO: Remove once CI\CD matures. + - name: Test Docker Build Local (Conditional) + if: ${{ env.PUBLISH_IMAGE == 'true' }} + run: | + docker build --no-cache -t ${{ env.CFM_TEST_IMAGE }} -f ./docker/Dockerfile . + docker rmi ${{ env.CFM_TEST_IMAGE }} + env: + CFM_TEST_IMAGE: cfm-test-image:v${{ env.NEXT_VERSION }} + + # Manually create separate GitHub Release assets for upload using source code with updated version number. + - name: Create Release Assets for Semantic Release (Conditional) + if: ${{ env.PUBLISH_IMAGE == 'true' }} + run: | + mkdir -p /tmp/dist + cd .. + cp -r ./cfm ./${{ env.ARCHIVE_FOLDER }} + zip -r /tmp/dist/${{ env.ARCHIVE_NAME }}.zip ${{ env.ARCHIVE_FOLDER }} -x "./${{ env.ARCHIVE_FOLDER }}/.git/*" -x "./${{ env.ARCHIVE_FOLDER }}/node_modules/*" + tar --exclude='./${{ env.ARCHIVE_FOLDER }}/.git' --exclude='./${{ env.ARCHIVE_FOLDER }}/node_modules' -czf /tmp/dist/${{ env.ARCHIVE_NAME }}.tar.gz ${{ env.ARCHIVE_FOLDER }} + cd cfm + env: + ARCHIVE_FOLDER: cfm-v${{ env.NEXT_VERSION }} + ARCHIVE_NAME: cfm-${{ env.NEXT_VERSION }}-versioned + + - name: Run Semantic Release (Conditional) + if: ${{ env.PUBLISH_IMAGE == 'true' }} + run: npx semantic-release + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Container Registry Login (Conditional) + if: ${{ env.PUBLISH_IMAGE == 'true' }} + uses: docker/login-action@v3 + with: + registry: ${{ env.IMAGE_REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Docker Image Build (Conditional) + if: ${{ env.PUBLISH_IMAGE == 'true' }} + run: docker build --no-cache -t ${{ env.CFM_NEXT_VERSION }} -t ${{ env.CFM_LATEST_VERSION }} -f ./docker/Dockerfile . + env: + CFM_NEXT_VERSION: ${{ env.IMAGE_NAME }}:v${{ env.NEXT_VERSION }} + CFM_LATEST_VERSION: ${{ env.IMAGE_NAME }}:latest + + - name: Docker Image Publish (Conditional) + if: ${{ env.PUBLISH_IMAGE == 'true' }} + run: docker push --all-tags ${{ env.IMAGE_NAME }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index bf30991..0000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,26 +0,0 @@ -on: - push: - # Sequence of patterns matched against refs/tags - tags: - - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10 - -name: Create Release - -jobs: - build: - name: Create Release - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v2 - - name: Create Release - id: create_release - uses: actions/create-release@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # This token is provided by Actions, you do not need to create your own token - with: - tag_name: ${{ github.ref }} - release_name: Release ${{ github.ref }} - draft: true - prerelease: false - diff --git a/.releaserc.yml b/.releaserc.yml new file mode 100644 index 0000000..0a14f6b --- /dev/null +++ b/.releaserc.yml @@ -0,0 +1,119 @@ +branches: ['main'] +ci: true +debug: true +dryRun: false +preset: 'conventionalcommits' + +# Specify plugins to override semantic-release defaults +plugins: +- "@semantic-release/commit-analyzer" +- "@semantic-release/release-notes-generator" +- "@semantic-release/github" +- "@semantic-release/exec" + +verifyConditions: + - '@semantic-release/github' + +analyzeCommits: + - path: '@semantic-release/commit-analyzer' + # Determine the type of release by analyzing commits with conventional-changelog + releaseRules: + - breaking: true + release: major + - type: build # Changes that affect the build system or external dependencies + # (example scopes: gulp, broccoli, npm) + release: patch + - type: chore # Other changes that don't modify src or test files + release: false + - type: ci # Changes to our CI configuration files and scripts + release: false + - type: docs # Documentation only changes + release: patch + - type: feat # A new feature + release: minor + - type: fix # A bug fix + release: patch + - type: perf # A code change that improves performance + release: patch + - type: refactor # A code change that neither fixes a bug nor adds a feature + release: false + - type: revert # Reverts a previous commit + release: patch + - type: style # Changes that do not affect the meaning of the code + # (white-space, formatting, missing semi-colons, etc) + release: false + - type: test # Adding missing tests or correcting existing tests + release: false + +verifyRelease: + - path: '@semantic-release/exec' + verifyReleaseCmd: 'echo "LAST_VERSION=${lastRelease.version}" >> $GITHUB_ENV && echo "NEXT_VERSION=${nextRelease.version}" >> $GITHUB_ENV' + +generateNotes: + - path: '@semantic-release/release-notes-generator' + # writerOpts: + # groupBy: 'type' + # commitGroupsSort: 'title' + # commitsSort: 'header' + # linkCompare: true + # linkReferences: true + # parserOpts: + # # detect JIRA issues in merge commits + # issuePrefixes: ['DEV-'] + # mergePattern: "^Merge branch '(.*)' into (.*)$" + # mergeCorrespondence: ['branch_src', 'branch_dst'] + presetConfig: + types: # looks like it only works with 'conventionalcommits' preset + - type: 'build' + section: '๐Ÿงฐ Build' + hidden: false + - type: 'chore' + section: '๐Ÿ Chore' + hidden: false + - type: 'ci' + section: '๐ŸฆŠ CI/CD' + hidden: false + - type: 'docs' + section: '๐Ÿ“” Docs' + hidden: false + - type: 'example' + section: '๐Ÿ“ Examples' + hidden: false + - type: 'feat' + section: '๐Ÿš€ Features' + hidden: false + - type: 'fix' + section: '๐Ÿ› ๏ธ Fixes' + hidden: false + - type: 'perf' + section: 'โฉ Performance' + - type: 'refactor' + section: 'โœ‚๏ธ Refactor' + hidden: false + - type: 'revert' + section: '๐Ÿ™…โ€โ™‚๏ธ Reverts' + - type: 'style' + section: '๐Ÿ’ˆ Style' + - type: 'test' + section: '๐Ÿงช Tests' + hidden: false + +prepare: + +publish: + #TODO: Can't figure out how to turn off the default, unversioned source code from also being added as assets. + - path: "@semantic-release/github" + assets: + - path: "/tmp/dist/*.tar.gz" + label: "Versioned source code (tar.gz)" + - path: "/tmp/dist/*.zip" + label: "Versioned source code (zip)" + +addChannel: + - path: '@semantic-release/github' + +success: + - path: '@semantic-release/github' + +fail: + - path: '@semantic-release/github' From 8846eb4d8e7378d7904e50a9d144b5195041e16e Mon Sep 17 00:00:00 2001 From: Mengling Ding <71745861+Meng-20@users.noreply.github.com> Date: Wed, 18 Sep 2024 11:42:46 -0600 Subject: [PATCH 03/12] feat: error handling improvements in back-end (#31) * feat: optimize the return in back-end * feat: remove the unused serviceError from the return structures * fix: fix the getMemory issue for blade adding --- pkg/backend/httpfish.go | 334 ++++++++++++++++------------------------ pkg/backend/ops.go | 71 ++++----- pkg/manager/blade.go | 2 +- 3 files changed, 164 insertions(+), 243 deletions(-) diff --git a/pkg/backend/httpfish.go b/pkg/backend/httpfish.go index 42c1541..0ce0ab5 100644 --- a/pkg/backend/httpfish.go +++ b/pkg/backend/httpfish.go @@ -523,7 +523,7 @@ func (service *httpfishService) CreateSession(ctx context.Context, settings *Con req.Insecure = true return service.CreateSession(ctx, settings, req) } else { - return &CreateSessionResponse{SessionId: session.SessionId, Status: "Failure", ServiceError: err}, err + return &CreateSessionResponse{SessionId: session.SessionId, Status: "Failure"}, err } } logger.V(4).Info("Session Created", "X-Auth-Token", session.xToken, "RedfishSessionId", session.RedfishSessionId) @@ -536,12 +536,13 @@ func (service *httpfishService) CreateSession(ctx context.Context, settings *Con _, exist := activeSessions[session.SessionId] if exist { - return &CreateSessionResponse{SessionId: session.SessionId, Status: "Duplicated", ServiceError: nil}, fmt.Errorf("endpoint already exist") + err := fmt.Errorf("endpoint already exist") + return &CreateSessionResponse{SessionId: session.SessionId, Status: "Duplicated"}, err } activeSessions[session.SessionId] = &session service.service.session = &session - return &CreateSessionResponse{SessionId: session.SessionId, Status: "Success", ServiceError: nil, ChassisSN: session.BladeSN, EnclosureSN: session.ApplianceSN}, nil + return &CreateSessionResponse{SessionId: session.SessionId, Status: "Success", ChassisSN: session.BladeSN, EnclosureSN: session.ApplianceSN}, nil } // DeleteSession: Delete a session previously established with an endpoint service @@ -560,10 +561,10 @@ func (service *httpfishService) DeleteSession(ctx context.Context, settings *Con // Let user know of delete backend failure. if response.err != nil { - return &DeleteSessionResponse{SessionId: session.SessionId, IpAddress: session.ip, Port: int32(session.port), Status: "Failure", ServiceError: response.err}, response.err + return &DeleteSessionResponse{SessionId: session.SessionId, IpAddress: session.ip, Port: int32(session.port), Status: "Failure"}, response.err } - return &DeleteSessionResponse{SessionId: session.SessionId, IpAddress: session.ip, Port: int32(session.port), Status: "Success", ServiceError: nil}, nil + return &DeleteSessionResponse{SessionId: session.SessionId, IpAddress: session.ip, Port: int32(session.port), Status: "Success"}, nil } // This struct holds the detail info of a specific resource block @@ -772,7 +773,7 @@ func (service *httpfishService) AllocateMemory(ctx context.Context, settings *Co if allocatedMebibytes == 0 || calcErr != nil { newErr := fmt.Errorf("problem during resource capacity calculations: %w", calcErr) logger.Error(newErr, "failure: allocate memory", "allocatedMebibytes", allocatedMebibytes, "req", req) - return &AllocateMemoryResponse{Status: "Failure", ServiceError: newErr}, newErr + return &AllocateMemoryResponse{Status: "Failure"}, newErr } jsonData := make(map[string]interface{}) @@ -783,7 +784,7 @@ func (service *httpfishService) AllocateMemory(ctx context.Context, settings *Co if response.err != nil { newErr := fmt.Errorf("backend session post failure(%s): %w", session.redfishPaths[PostResourceKey], response.err) logger.Error(newErr, "failure: allocate memory", "req", req, "allocatedMebibytes", allocatedMebibytes) - return &AllocateMemoryResponse{Status: "Failure", ServiceError: newErr}, newErr + return &AllocateMemoryResponse{Status: "Failure"}, newErr } //extract the memorychunk Id @@ -791,7 +792,7 @@ func (service *httpfishService) AllocateMemory(ctx context.Context, settings *Co memoryId := getIdFromOdataId(uriOfMemorychunkId[0]) session.memoryChunkPath[memoryId] = uriOfMemorychunkId[0] - return &AllocateMemoryResponse{SizeMiB: allocatedMebibytes, MemoryId: memoryId, Status: "Success", ServiceError: nil}, nil + return &AllocateMemoryResponse{SizeMiB: allocatedMebibytes, MemoryId: memoryId, Status: "Success"}, nil } // AllocateMemoryByResource: Create a new memory region using user-specified resource blocks @@ -810,7 +811,7 @@ func (service *httpfishService) AllocateMemoryByResource(ctx context.Context, se if response.err != nil { newErr := fmt.Errorf("backend session get failure(%s): %w", backendResourceUri, response.err) logger.Error(newErr, "failure: allocate memory by resource", "req", req) - return &AllocateMemoryByResourceResponse{Status: "Failure", ServiceError: newErr}, newErr + return &AllocateMemoryByResourceResponse{Status: "Failure"}, newErr } // check for a valid composition state @@ -818,7 +819,7 @@ func (service *httpfishService) AllocateMemoryByResource(ctx context.Context, se if statusErr != nil { newErr := fmt.Errorf("CompositionStatus not found(%s): %w", backendResourceUri, statusErr) logger.Error(newErr, "failure: allocate memory by resource", "req", req) - return &AllocateMemoryByResourceResponse{Status: "Failure", ServiceError: newErr}, newErr + return &AllocateMemoryByResourceResponse{Status: "Failure"}, newErr } compositionState := compositionStatus.(map[string]interface{})["CompositionState"].(string) @@ -828,7 +829,7 @@ func (service *httpfishService) AllocateMemoryByResource(ctx context.Context, se if *resourceState != ResourceUnused { newErr := fmt.Errorf("resource not accessible(%s): session [%s] state [%s] ", backendResourceUri, session.SessionId, *resourceState) logger.Error(newErr, "failure: allocate memory by resource") - return &AllocateMemoryByResourceResponse{Status: "Failure", ServiceError: newErr}, newErr + return &AllocateMemoryByResourceResponse{Status: "Failure"}, newErr } // save off the backend resource uri, used later in POST @@ -850,7 +851,7 @@ func (service *httpfishService) AllocateMemoryByResource(ctx context.Context, se if response.err != nil { newErr := fmt.Errorf("backend session post failure(%s): %w", session.redfishPaths[PostResourceKey], response.err) logger.Error(newErr, "failure: allocate memory by resource", "req", req) - return &AllocateMemoryByResourceResponse{Status: "Failure", ServiceError: newErr}, newErr + return &AllocateMemoryByResourceResponse{Status: "Failure"}, newErr } //extract the memorychunk Id @@ -858,7 +859,7 @@ func (service *httpfishService) AllocateMemoryByResource(ctx context.Context, se memoryId := getIdFromOdataId(uriOfMemorychunkId[0]) session.memoryChunkPath[memoryId] = uriOfMemorychunkId[0] - return &AllocateMemoryByResourceResponse{MemoryId: memoryId, Status: "Success", ServiceError: nil}, nil + return &AllocateMemoryByResourceResponse{MemoryId: memoryId, Status: "Success"}, nil } // Extract the corresponding endpoint uri of the input port @@ -958,14 +959,14 @@ func (service *httpfishService) AssignMemory(ctx context.Context, settings *Conf if errOfTargetEndpoint != nil { newErr := fmt.Errorf("backend session failure(%s): %w", session.redfishPaths[FabricConnectionsKey], errOfTargetEndpoint) logger.Error(newErr, "failure: assign memory", "req", req) - return &AssignMemoryResponse{Status: "Failure", ServiceError: newErr}, newErr + return &AssignMemoryResponse{Status: "Failure"}, newErr } availableErr := session.isEndpointAvailable(*uriOfTargetEndpoint) if availableErr != nil { newErr := fmt.Errorf("backend session failure(%s): %w", session.redfishPaths[FabricConnectionsKey], availableErr) logger.Error(newErr, "failure: assign memory", "req", req) - return &AssignMemoryResponse{Status: "Failure", ServiceError: newErr}, newErr + return &AssignMemoryResponse{Status: "Failure"}, newErr } jsonDataOfTargetEndpoint := make([]map[string]interface{}, 1) @@ -980,10 +981,10 @@ func (service *httpfishService) AssignMemory(ctx context.Context, settings *Conf if response.err != nil { newErr := fmt.Errorf("backend session post failure(%s): %w", session.redfishPaths[FabricConnectionsKey], response.err) logger.Error(newErr, "failure: assign memory", "req", req) - return &AssignMemoryResponse{Status: "Failure", ServiceError: newErr}, newErr + return &AssignMemoryResponse{Status: "Failure"}, newErr } - return &AssignMemoryResponse{Status: "Success", ServiceError: nil}, nil + return &AssignMemoryResponse{Status: "Success"}, nil } // UnassignMemory: Delete(Unassign) a connection between a memory region and it's local hardware port. If no connection found, no action taken. @@ -999,14 +1000,14 @@ func (service *httpfishService) UnassignMemory(ctx context.Context, settings *Co if responseGetAllConnections.err != nil { newErr := fmt.Errorf("backend session get failure(%s): %w", session.redfishPaths[FabricConnectionsKey], responseGetAllConnections.err) logger.Error(newErr, "failure: unassign memory", "req", req) - return &UnassignMemoryResponse{Status: "Failure", ServiceError: newErr}, newErr + return &UnassignMemoryResponse{Status: "Failure"}, newErr } connections, err := responseGetAllConnections.arrayFromJSON("Members") if err != nil { newErr := fmt.Errorf("response parsing failure('Members'): %w", err) logger.Error(newErr, "failure: unassign memory", "req", req) - return &UnassignMemoryResponse{Status: "Failure", ServiceError: newErr}, newErr + return &UnassignMemoryResponse{Status: "Failure"}, newErr } // Search all session connections for the specified memoryId @@ -1021,14 +1022,14 @@ func (service *httpfishService) UnassignMemory(ctx context.Context, settings *Co if responseGetConnection.err != nil { newErr := fmt.Errorf("backend session get failure(%s): %w", connectionUri, responseGetConnection.err) logger.Error(newErr, "failure: unassign memory", "req", req) - return &UnassignMemoryResponse{Status: "Failure", ServiceError: newErr}, newErr + return &UnassignMemoryResponse{Status: "Failure"}, newErr } mcInfos, mciErr := responseGetConnection.arrayFromJSON("MemoryChunkInfo") if mciErr != nil { newErr := fmt.Errorf("response parsing failure('MemoryChunkInfo'): %w", mciErr) logger.Error(newErr, "failure: unassign memory", "req", req) - return &UnassignMemoryResponse{Status: "Failure", ServiceError: newErr}, newErr + return &UnassignMemoryResponse{Status: "Failure"}, newErr } // Search for memoryId on the current connection @@ -1050,7 +1051,7 @@ func (service *httpfishService) UnassignMemory(ctx context.Context, settings *Co if responseGetEndpt.err != nil { newErr := fmt.Errorf("backend session get failure(%s): %w", endptUri, responseGetEndpt.err) logger.Error(newErr, "failure: unassign memory", "req", req) - return &UnassignMemoryResponse{Status: "Failure", ServiceError: newErr}, newErr + return &UnassignMemoryResponse{Status: "Failure"}, newErr } endptLinks, _ := responseGetEndpt.valueFromJSON("Links") @@ -1067,7 +1068,7 @@ func (service *httpfishService) UnassignMemory(ctx context.Context, settings *Co if responseDeleteConnection.err != nil { newErr := fmt.Errorf("backend session delete failure(%s): %w", connectionUri, responseDeleteConnection.err) logger.Error(newErr, "failure: unassign memory", "req", req) - return &UnassignMemoryResponse{Status: "Failure", ServiceError: newErr}, newErr + return &UnassignMemoryResponse{Status: "Failure"}, newErr } logger.V(4).Info("unassign memory: connection deleted", "connectionUri", connectionUri, "req", req) @@ -1084,7 +1085,7 @@ func (service *httpfishService) UnassignMemory(ctx context.Context, settings *Co if !foundPort { newErr := fmt.Errorf("connection mismatch: portId(%s) not connected to memoryId(%s)", req.MemoryId, req.PortId) logger.Error(newErr, "failure: unassign memory", "req", req) - return &UnassignMemoryResponse{Status: "Failure", ServiceError: newErr}, newErr + return &UnassignMemoryResponse{Status: "Failure"}, newErr } break @@ -1099,10 +1100,10 @@ func (service *httpfishService) UnassignMemory(ctx context.Context, settings *Co if !foundMemory { newErr := fmt.Errorf("memoryId(%s) not found", req.MemoryId) logger.Error(newErr, "failure: unassign memory", "req", req) - return &UnassignMemoryResponse{Status: "Failure", ServiceError: newErr}, newErr + return &UnassignMemoryResponse{Status: "Failure"}, newErr } - return &UnassignMemoryResponse{Status: "Success", ServiceError: nil}, nil + return &UnassignMemoryResponse{Status: "Success"}, nil } // GetMemoryResourceBlocks: Request Memory Resource Block information from the backends @@ -1118,7 +1119,7 @@ func (service *httpfishService) GetMemoryResourceBlocks(ctx context.Context, set response := session.query(HTTPOperation.GET, session.redfishPaths[ResourceBlocksKey]) if response.err != nil { - return &MemoryResourceBlocksResponse{Status: "Failure", ServiceError: response.err}, response.err + return &MemoryResourceBlocksResponse{Status: "Failure"}, response.err } resourceBlocks, _ := response.arrayFromJSON("Members") @@ -1135,7 +1136,7 @@ func (service *httpfishService) GetMemoryResourceBlocks(ctx context.Context, set memoryResources = append(memoryResources, getIdFromOdataId(uri)) } - return &MemoryResourceBlocksResponse{MemoryResources: memoryResources, Status: "Success", ServiceError: nil}, nil + return &MemoryResourceBlocksResponse{MemoryResources: memoryResources, Status: "Success"}, nil } // GetMemoryResourceBlockById: Request a particular Memory Resource Block information by ID from the backends @@ -1156,7 +1157,7 @@ func (service *httpfishService) GetMemoryResourceBlockById(ctx context.Context, if response.err != nil { newErr := fmt.Errorf("backend session get failure(%s): %w", uri, response.err) logger.Error(newErr, "failure: get resource by id", "req", req) - return &MemoryResourceBlockByIdResponse{Status: "Failure", ServiceError: newErr}, newErr + return &MemoryResourceBlockByIdResponse{Status: "Failure"}, newErr } // Update CompositionState using the Reserved and CompositionState values from Redfish @@ -1216,7 +1217,7 @@ func (service *httpfishService) GetMemoryResourceBlockById(ctx context.Context, memoryResourceBlock.CapacityMiB = int32(totalMebibytes) - return &MemoryResourceBlockByIdResponse{MemoryResourceBlock: memoryResourceBlock, Status: "Success", ServiceError: nil}, nil + return &MemoryResourceBlockByIdResponse{MemoryResourceBlock: memoryResourceBlock, Status: "Success"}, nil } func findResourceState(compositionState *string, reserved bool) *ResourceState { @@ -1243,31 +1244,28 @@ func (service *httpfishService) GetPorts(ctx context.Context, settings *Configur logger.V(4).Info("====== GetPorts ======") logger.V(4).Info("GetPorts", "req", req) - var ret = GetPortsResponse{ - Status: "Failure", - } - session := service.service.session.(*Session) // Allow blade sessions only _, keyExist := session.redfishPaths[FabricPortsKey] if !keyExist { - ret.Status = "Not Supported" - ret.ServiceError = fmt.Errorf("session (%s) does not support .../fabrics/.../switches/.../ports", session.SessionId) - logger.Error(ret.ServiceError, "failure: get ports") - return &ret, ret.ServiceError + newErr := fmt.Errorf("session (%s) does not support .../fabrics/.../switches/.../ports", session.SessionId) + logger.Error(newErr, "failure: get ports") + return &GetPortsResponse{Status: "Not Supported"}, newErr } response := session.query(HTTPOperation.GET, session.redfishPaths[FabricPortsKey]) if response.err != nil { - ret.ServiceError = response.err - logger.Error(ret.ServiceError, "failure: get ports") - return &ret, ret.ServiceError + newErr := response.err + logger.Error(newErr, "failure: get ports") + return &GetPortsResponse{Status: "Failure"}, newErr } ports, _ := response.arrayFromJSON("Members") + var portIds []string + for _, port := range ports { uri := port.(map[string]interface{})["@odata.id"].(string) tokens := strings.Split(uri, "/") @@ -1277,13 +1275,11 @@ func (service *httpfishService) GetPorts(ctx context.Context, settings *Configur portId := tokens[len(tokens)-1] if len(portId) > 0 { - ret.PortIds = append(ret.PortIds, portId) + portIds = append(portIds, portId) } } - ret.Status = "Success" - - return &ret, nil + return &GetPortsResponse{PortIds: portIds, Status: "Success"}, nil } // GetHostPortPcieDevices: Request pcie devices, each representing a physical host port, from the backend @@ -1292,28 +1288,24 @@ func (service *httpfishService) GetHostPortPcieDevices(ctx context.Context, sett logger.V(4).Info("====== GetHostPortPcieDevices ======") logger.V(4).Info("GetHostPortPcieDevices", "req", req) - var ret = GetPortsResponse{ - Status: "Failure", - } - session := service.service.session.(*Session) // Allow host sessions only _, keyExist := session.redfishPaths[ChassisPcieDevKey] if !keyExist { - ret.Status = "Not Supported" - ret.ServiceError = fmt.Errorf("session (%s) does not support .../chassis/.../pciedevices", session.SessionId) - logger.Error(ret.ServiceError, "failure: get port pcie devices") - return &ret, ret.ServiceError + newErr := fmt.Errorf("session (%s) does not support .../chassis/.../pciedevices", session.SessionId) + logger.Error(newErr, "failure: get port pcie devices") + return &GetPortsResponse{Status: "Not Supported"}, newErr } response := session.query(HTTPOperation.GET, session.redfishPaths[ChassisPcieDevKey]) if response.err != nil { - ret.ServiceError = response.err - logger.Error(ret.ServiceError, "failure: get ports") - return &ret, ret.ServiceError + newErr := response.err + logger.Error(newErr, "failure: get ports") + return &GetPortsResponse{Status: "Failure"}, newErr } + var portIds []string ports, _ := response.arrayFromJSON("Members") for _, port := range ports { uri := port.(map[string]interface{})["@odata.id"].(string) @@ -1324,13 +1316,11 @@ func (service *httpfishService) GetHostPortPcieDevices(ctx context.Context, sett pcieId := tokens[len(tokens)-1] if len(pcieId) > 0 { - ret.PortIds = append(ret.PortIds, pcieId) + portIds = append(portIds, pcieId) } } - ret.Status = "Success" - - return &ret, nil + return &GetPortsResponse{PortIds: portIds, Status: "Success"}, nil } // GetPortDetails: Request Ports info from the backend @@ -1339,39 +1329,34 @@ func (service *httpfishService) GetPortDetails(ctx context.Context, settings *Co logger.V(4).Info("====== GetPortDetails ======") logger.V(4).Info("GetPortDetails", "req", req) - var ret = GetPortDetailsResponse{ - PortInformation: PortInformation{}, - Status: "Failure", - } - session := service.service.session.(*Session) // Allow blade sessions only _, keyExist := session.redfishPaths[FabricPortsKey] if !keyExist { - ret.Status = "Not Supported" - ret.ServiceError = fmt.Errorf("session (%s) does not support .../fabrics/.../switches/.../ports", session.SessionId) - logger.Error(ret.ServiceError, "failure: get port details", "req", req) - return &ret, ret.ServiceError + newErr := fmt.Errorf("session (%s) does not support .../fabrics/.../switches/.../ports", session.SessionId) + logger.Error(newErr, "failure: get port details", "req", req) + return &GetPortDetailsResponse{Status: "Not Supported"}, newErr } response := session.query(HTTPOperation.GET, session.buildPath(FabricPortsKey, req.PortId)) if response.err != nil { - ret.ServiceError = response.err - logger.Error(ret.ServiceError, "failure: get port details", "req", req) - return &ret, ret.ServiceError + newErr := response.err + logger.Error(newErr, "failure: get port details", "req", req) + return &GetPortDetailsResponse{Status: "Failure"}, newErr } + var portInformation PortInformation id, _ := response.stringFromJSON("Id") - ret.PortInformation.Id = id - ret.PortInformation.PortProtocol, _ = response.stringFromJSON("PortProtocol") - ret.PortInformation.PortMedium, _ = response.stringFromJSON("PortMedium") + portInformation.Id = id + portInformation.PortProtocol, _ = response.stringFromJSON("PortProtocol") + portInformation.PortMedium, _ = response.stringFromJSON("PortMedium") width, err := response.floatFromJSON("ActiveWidth") if err == nil { - ret.PortInformation.Width = int32(width) + portInformation.Width = int32(width) } - ret.PortInformation.LinkStatus, _ = response.stringFromJSON("LinkStatus") - ret.PortInformation.LinkState, _ = response.stringFromJSON("LinkState") + portInformation.LinkStatus, _ = response.stringFromJSON("LinkStatus") + portInformation.LinkState, _ = response.stringFromJSON("LinkState") status, _ := response.valueFromJSON("Status") @@ -1379,37 +1364,36 @@ func (service *httpfishService) GetPortDetails(ctx context.Context, settings *Co state := status.(map[string]interface{})["State"].(string) healthAndState := fmt.Sprintf("%s/%s", health, state) - ret.PortInformation.StatusHealth = health - ret.PortInformation.StatusState = state - ret.Status = healthAndState + portInformation.StatusHealth = health + portInformation.StatusState = state portField, err := response.valueFromJSON("Port") if err == nil { speedFloat, _ := portField.(map[string]interface{})["CurrentSpeedGbps"].(float64) - ret.PortInformation.CurrentSpeedGbps = int32(speedFloat) + portInformation.CurrentSpeedGbps = int32(speedFloat) } // Extract GCXLID from endpoint uriOfTargetEndpoint, errOfTargetEndpoint := session.getEndpointUriFromPort(id) if errOfTargetEndpoint != nil { - ret.ServiceError = errOfTargetEndpoint - logger.Error(ret.ServiceError, "failure: get port details", "req", req) - return &ret, ret.ServiceError + newErr := errOfTargetEndpoint + logger.Error(newErr, "failure: get port details", "req", req) + return &GetPortDetailsResponse{Status: "Failure"}, newErr } response = session.query(HTTPOperation.GET, *uriOfTargetEndpoint) if response.err != nil { - ret.ServiceError = response.err - logger.Error(ret.ServiceError, "failure: get port details", "req", req) - return &ret, ret.ServiceError + newErr := errOfTargetEndpoint + logger.Error(newErr, "failure: get port details", "req", req) + return &GetPortDetailsResponse{Status: "Failure"}, newErr } identifiers, _ := response.valueFromJSON("Identifiers") - ret.PortInformation.GCxlId = identifiers.([]interface{})[0].(map[string]interface{})["DurableName"].(string) + portInformation.GCxlId = identifiers.([]interface{})[0].(map[string]interface{})["DurableName"].(string) //Note: "PortInformation.LinkedPortUri" can't be determined here. Handled separately. - return &ret, ret.ServiceError + return &GetPortDetailsResponse{PortInformation: portInformation, Status: healthAndState}, nil } // GetHostPortSnById: Request the serial number from a specific port (ie - pcie device) and cxl host @@ -1418,42 +1402,34 @@ func (service *httpfishService) GetHostPortSnById(ctx context.Context, settings logger.V(4).Info("====== GetHostPortSnById ======") logger.V(4).Info("GetHostPortSnById", "req", req) - var ret = GetHostPortSnByIdResponse{ - Status: "Failure", - } - session := service.service.session.(*Session) // Allow host sessions only _, keyExist := session.redfishPaths[ChassisPcieDevKey] if !keyExist { - ret.Status = "Not Supported" - ret.ServiceError = fmt.Errorf("session (%s) does not support .../chassis/.../pciedevices", session.SessionId) - logger.Error(ret.ServiceError, "failure: get host port sn by id", "req", req) - return &ret, ret.ServiceError + newErr := fmt.Errorf("session (%s) does not support .../chassis/.../pciedevices", session.SessionId) + logger.Error(newErr, "failure: get host port sn by id", "req", req) + return &GetHostPortSnByIdResponse{Status: "Not Supported"}, newErr } // Query port deviceUri := session.buildPath(ChassisPcieDevKey, req.PortId) response := session.query(HTTPOperation.GET, deviceUri) if response.err != nil { - ret.ServiceError = fmt.Errorf("session (%s) query failure (%s) for port (%s): %w", session.SessionId, deviceUri, req.PortId, response.err) - logger.Error(ret.ServiceError, "failure: get host port sn by id") - return &ret, ret.ServiceError + newErr := fmt.Errorf("session (%s) query failure (%s) for port (%s): %w", session.SessionId, deviceUri, req.PortId, response.err) + logger.Error(newErr, "failure: get host port sn by id") + return &GetHostPortSnByIdResponse{Status: "Failure"}, newErr } // Extract the SN sn, err := response.stringFromJSON("SerialNumber") if err != nil { - ret.ServiceError = fmt.Errorf("session (%s) query failure (%s) for port (%s): SerialNumber NOT found: %w", session.SessionId, deviceUri, req.PortId, response.err) - logger.Error(ret.ServiceError, "failure: get host port sn by id") - return &ret, ret.ServiceError + newErr := fmt.Errorf("session (%s) query failure (%s) for port (%s): SerialNumber NOT found: %w", session.SessionId, deviceUri, req.PortId, response.err) + logger.Error(newErr, "failure: get host port sn by id") + return &GetHostPortSnByIdResponse{Status: "Failure"}, newErr } - ret.SerialNumber = sn - ret.Status = "Success" - - return &ret, nil + return &GetHostPortSnByIdResponse{SerialNumber: sn, Status: "Success"}, nil } // GetMemoryDevices: Delete memory region info by memory id @@ -1467,7 +1443,7 @@ func (service *httpfishService) GetMemoryDevices(ctx context.Context, settings * if response.err != nil { newErr := fmt.Errorf("backend get failure [%s]: %w", session.redfishPaths[ChassisPcieDevKey], response.err) logger.Error(newErr, "failure: get memory devices") - return &GetMemoryDevicesResponse{Status: "Failure", ServiceError: newErr}, newErr + return &GetMemoryDevicesResponse{Status: "Failure"}, newErr } // Mapping of physical device IDs (keys) to a slice of logical device IDs (values) @@ -1484,14 +1460,14 @@ func (service *httpfishService) GetMemoryDevices(ctx context.Context, settings * if response.err != nil { newErr := fmt.Errorf("backend get failure [%s]: %w", session.buildPath("ChassisPcieDev", phyDevId), response.err) logger.Error(newErr, "failure: get memory devices") - return &GetMemoryDevicesResponse{Status: "Failure", ServiceError: newErr}, newErr + return &GetMemoryDevicesResponse{Status: "Failure"}, newErr } logicalDevicesUri, err := response.odataStringFromJSON("CXLLogicalDevices") if err != nil { newErr := fmt.Errorf("backend response key ['CXLLogicalDevices'] not found for uri [%s]: %w", logicalDevicesUri, err) logger.Error(newErr, "failure: get memory devices") - return &GetMemoryDevicesResponse{Status: "Failure", ServiceError: newErr}, newErr + return &GetMemoryDevicesResponse{Status: "Failure"}, newErr } cxlCollection := session.query(HTTPOperation.GET, logicalDevicesUri) @@ -1502,7 +1478,7 @@ func (service *httpfishService) GetMemoryDevices(ctx context.Context, settings * } } - return &GetMemoryDevicesResponse{DeviceIdMap: deviceIdMap, Status: "Success", ServiceError: nil}, nil + return &GetMemoryDevicesResponse{DeviceIdMap: deviceIdMap, Status: "Success"}, nil } // pcieGenToSpeed: convert PCIe device generation string to speed @@ -1533,20 +1509,19 @@ func (service *httpfishService) GetMemoryDeviceDetails(ctx context.Context, sett if response.err != nil { newErr := fmt.Errorf("backend get failure [%s]: %w", pcieDeviceUri, response.err) logger.Error(newErr, "failure: get memory device details") - return &GetMemoryDeviceDetailsResponse{Status: "Failure", ServiceError: newErr}, newErr + return &GetMemoryDeviceDetailsResponse{Status: "Failure"}, newErr } status, _ := response.valueFromJSON("Status") memDev := GetMemoryDeviceDetailsResponse{ - Status: status.(map[string]interface{})["State"].(string), - ServiceError: nil, + Status: status.(map[string]interface{})["State"].(string), } sn, err := response.stringFromJSON("SerialNumber") if err != nil { newErr := fmt.Errorf("backend response key ['SerialNumber'] not found for uri [%s]: %w", pcieDeviceUri, err) logger.Error(newErr, "failure: get memory device details") - return &GetMemoryDeviceDetailsResponse{Status: "Failure", ServiceError: newErr}, newErr + return &GetMemoryDeviceDetailsResponse{Status: "Failure"}, newErr } memDev.SerialNumber = sn @@ -1580,7 +1555,7 @@ func (service *httpfishService) GetMemoryDeviceDetails(ctx context.Context, sett if err != nil { newErr := fmt.Errorf("backend response key ['CXLLogicalDevices'] not found for uri [%s]: %w", logicalDevicesUri, err) logger.Error(newErr, "failure: get memory device details") - return &GetMemoryDeviceDetailsResponse{Status: "Failure", ServiceError: newErr}, newErr + return &GetMemoryDeviceDetailsResponse{Status: "Failure"}, newErr } logicalDeviceUri := fmt.Sprintf("%s/%s", logicalDevicesUri, req.LogicalDeviceId) @@ -1588,7 +1563,7 @@ func (service *httpfishService) GetMemoryDeviceDetails(ctx context.Context, sett if logicalDevice.err != nil { newErr := fmt.Errorf("backend get failure [%s]: %w", logicalDeviceUri, logicalDevice.err) logger.Error(newErr, "failure: get memory device details") - return &GetMemoryDeviceDetailsResponse{Status: "Failure", ServiceError: newErr}, newErr + return &GetMemoryDeviceDetailsResponse{Status: "Failure"}, newErr } memSize, _ := logicalDevice.valueFromJSON("MemorySizeMiB") @@ -1601,28 +1576,6 @@ func (service *httpfishService) GetMemoryDeviceDetails(ctx context.Context, sett return &memDev, nil } -// Extract the connection info for a specific endpoint -func (session *Session) getConnectionFromEndpoint(uriEndpoint string) (*string, error) { - // Get the endpoint - responseOfEndpoint := session.query(HTTPOperation.GET, uriEndpoint) - if responseOfEndpoint.err != nil { - return nil, fmt.Errorf("failed to get endpoint") - } - - // Extra the corresponding endpoint - links, _ := responseOfEndpoint.valueFromJSON("Links") - uriOfConnection := links.(map[string]interface{})["Connections"].([]interface{})[0].(map[string]interface{})["@odata.id"].(string) - // if the connection is empty, no connection - if &uriOfConnection == nil { - return nil, fmt.Errorf("endpoint has no connection") - } - - // Get the connection id - connectionId := getIdFromOdataId(uriOfConnection) - - return &connectionId, nil -} - // FreeMemoryById: Delete memory region (memory chunk) by memory id func (service *httpfishService) FreeMemoryById(ctx context.Context, settings *ConfigurationSettings, req *FreeMemoryRequest) (*FreeMemoryResponse, error) { logger := klog.FromContext(ctx) @@ -1637,16 +1590,12 @@ func (service *httpfishService) FreeMemoryById(ctx context.Context, settings *Co if response.err != nil { newErr := fmt.Errorf("backend session delete failure(%s): %w", session.buildPath(SystemMemoryChunksKey, req.MemoryId), response.err) logger.Error(newErr, "failure: free memory by id", "req", req) - return &FreeMemoryResponse{Status: "Failure", ServiceError: newErr}, newErr + return &FreeMemoryResponse{Status: "Failure"}, newErr } delete(session.memoryChunkPath, req.MemoryId) - freeMemoryResponse := &FreeMemoryResponse{ - Status: "Success", - ServiceError: nil} - - return freeMemoryResponse, nil + return &FreeMemoryResponse{Status: "Success"}, nil } // GetMemoryById: Get a specific memory region info by memory id @@ -1654,15 +1603,11 @@ func (service *httpfishService) GetMemoryById(ctx context.Context, setting *Conf logger := klog.FromContext(ctx) logger.V(4).Info("====== GetMemoryById ======") logger.V(4).Info("get memory by id", "request", req) - - memoryByIdResponse := GetMemoryByIdResponse{ - MemoryRegion: TypeMemoryRegion{ - MemoryId: req.MemoryId, - Status: "Success", - Type: MemoryType(MEMORYTYPE_MEMORY_TYPE_REGION), - SizeMiB: 0, - }, - Status: "Success", + memoryRegion := TypeMemoryRegion{ + MemoryId: req.MemoryId, + Status: "Failure", + Type: MemoryType(MEMORYTYPE_MEMORY_TYPE_REGION), + SizeMiB: 0, } session := service.service.session.(*Session) @@ -1675,28 +1620,22 @@ func (service *httpfishService) GetMemoryById(ctx context.Context, setting *Conf path, exist = session.memoryChunkPath[req.MemoryId] if !exist { - memoryByIdResponse.Status = "Not Found" - memoryByIdResponse.MemoryRegion.Status = "Failure" - memoryByIdResponse.ServiceError = fmt.Errorf("memory (%s) does not exist", req.MemoryId) - - return &memoryByIdResponse, memoryByIdResponse.ServiceError + newErr := fmt.Errorf("memory (%s) does not exist", req.MemoryId) + return &GetMemoryByIdResponse{MemoryRegion: memoryRegion, Status: "Not Found"}, newErr } } response := session.query(HTTPOperation.GET, path) if response.err != nil { - memoryByIdResponse.Status = "Failure" - memoryByIdResponse.MemoryRegion.Status = "Failure" - memoryByIdResponse.ServiceError = response.err - - return &memoryByIdResponse, memoryByIdResponse.ServiceError + newErr := response.err + return &GetMemoryByIdResponse{MemoryRegion: memoryRegion, Status: "Failure"}, newErr } - memoryByIdResponse.MemoryRegion.MemoryId, _ = response.stringFromJSON("Id") + memoryRegion.MemoryId, _ = response.stringFromJSON("Id") val, _ := response.valueFromJSON("MemoryChunkSizeMiB") - memoryByIdResponse.MemoryRegion.SizeMiB = int32(val.(float64)) + memoryRegion.SizeMiB = int32(val.(float64)) if strings.Contains(path, "CXL") { // host cxl memory - memoryByIdResponse.MemoryRegion.Type = MemoryType(MEMORYTYPE_MEMORY_TYPE_CXL) + memoryRegion.Type = MemoryType(MEMORYTYPE_MEMORY_TYPE_CXL) // Check if performacne metric is reported oemField, _ := response.valueFromJSON("Oem") if oemField != nil { @@ -1710,16 +1649,17 @@ func (service *httpfishService) GetMemoryById(ctx context.Context, setting *Conf */ bwStr := oemField.(map[string]interface{})["Seagate"].(map[string]interface{})["Bandwidth"].(string) bwFloat, _ := strconv.ParseFloat(strings.Split(bwStr, " ")[0], 64) - memoryByIdResponse.MemoryRegion.Bandwidth = int32(bwFloat) + memoryRegion.Bandwidth = int32(bwFloat) latStr := oemField.(map[string]interface{})["Seagate"].(map[string]interface{})["Latency"].(string) latInt64, _ := strconv.ParseInt(strings.Split(latStr, " ")[0], 10, 64) - memoryByIdResponse.MemoryRegion.Latency = int32(latInt64) + memoryRegion.Latency = int32(latInt64) } links, _ := response.valueFromJSON("Links") endpoints, ok := links.(map[string]interface{})["Endpoints"].([]interface{}) if !ok || len(endpoints) >= 2 { - return &memoryByIdResponse, fmt.Errorf("invalid endpoints") + newErr := fmt.Errorf("invalid endpoints") + return &GetMemoryByIdResponse{MemoryRegion: memoryRegion, Status: "Failure"}, newErr } // This entire IF is about finding the host port associated with the requested memoryId @@ -1728,13 +1668,15 @@ func (service *httpfishService) GetMemoryById(ctx context.Context, setting *Conf response := session.query(HTTPOperation.GET, uriSystemMemory) if response.err != nil { - return &memoryByIdResponse, fmt.Errorf("get [%s] failure: %w", uriSystemMemory, response.err) + newErr := fmt.Errorf("get [%s] failure: %w", uriSystemMemory, response.err) + return &GetMemoryByIdResponse{MemoryRegion: memoryRegion, Status: "Failure"}, newErr } links, _ = response.valueFromJSON("Links") sources := links.(map[string]interface{})["MemoryMediaSources"].([]interface{}) if !ok || len(sources) >= 2 { - return &memoryByIdResponse, fmt.Errorf("invalid memory media sources") + newErr := fmt.Errorf("invalid memory media sources") + return &GetMemoryByIdResponse{MemoryRegion: memoryRegion, Status: "Failure"}, newErr } if len(sources) != 0 { @@ -1742,13 +1684,15 @@ func (service *httpfishService) GetMemoryById(ctx context.Context, setting *Conf response = session.query(HTTPOperation.GET, uriChassisMemoryChunks) if response.err != nil { - return &memoryByIdResponse, fmt.Errorf("get [%s] failure: %w", uriChassisMemoryChunks, response.err) + newErr := fmt.Errorf("get [%s] failure: %w", uriChassisMemoryChunks, response.err) + return &GetMemoryByIdResponse{MemoryRegion: memoryRegion, Status: "Failure"}, newErr } links, _ = response.valueFromJSON("Links") devices := links.(map[string]interface{})["CXLLogicalDevices"].([]interface{}) if !ok || len(devices) >= 2 { - return &memoryByIdResponse, fmt.Errorf("invalid cxl logical devices") + newErr := fmt.Errorf("invalid cxl logical devices") + return &GetMemoryByIdResponse{MemoryRegion: memoryRegion, Status: "Failure"}, newErr } if len(devices) != 0 { @@ -1756,20 +1700,21 @@ func (service *httpfishService) GetMemoryById(ctx context.Context, setting *Conf elements := strings.Split(uriCxlLogicalDevice, "/") if len(elements) < 8 { - return &memoryByIdResponse, fmt.Errorf("invalid cxl logical devices uri [%s]", uriCxlLogicalDevice) + newErr := fmt.Errorf("invalid cxl logical devices uri [%s]", uriCxlLogicalDevice) + return &GetMemoryByIdResponse{MemoryRegion: memoryRegion, Status: "Failure"}, newErr } - memoryByIdResponse.MemoryRegion.PortId = elements[len(elements)-3] - memoryByIdResponse.MemoryRegion.LogicalDeviceId = elements[len(elements)-1] + memoryRegion.PortId = elements[len(elements)-3] + memoryRegion.LogicalDeviceId = elements[len(elements)-1] } } } - return &memoryByIdResponse, memoryByIdResponse.ServiceError + return &GetMemoryByIdResponse{MemoryRegion: memoryRegion, Status: "Success"}, nil } else if strings.Contains(path, "DIMMs") { // host local memory - memoryByIdResponse.MemoryRegion.Type = MemoryType(MEMORYTYPE_MEMORY_TYPE_LOCAL) - return &memoryByIdResponse, memoryByIdResponse.ServiceError + memoryRegion.Type = MemoryType(MEMORYTYPE_MEMORY_TYPE_LOCAL) + return &GetMemoryByIdResponse{MemoryRegion: memoryRegion, Status: "Success"}, nil } else { // memory appliance memory links, _ := response.valueFromJSON("Links") @@ -1801,11 +1746,11 @@ func (service *httpfishService) GetMemoryById(ctx context.Context, setting *Conf return nil, fmt.Errorf("invalid port uri [%s]", uriPort) } - memoryByIdResponse.MemoryRegion.PortId = elements[len(elements)-1] + memoryRegion.PortId = elements[len(elements)-1] } } - return &memoryByIdResponse, memoryByIdResponse.ServiceError + return &GetMemoryByIdResponse{MemoryRegion: memoryRegion, Status: "Success"}, nil } } @@ -1815,20 +1760,15 @@ func (service *httpfishService) GetMemory(ctx context.Context, settings *Configu logger.V(4).Info("====== GetMemory ======") logger.V(4).Info("get memory", "request", req) - memoryResponse := GetMemoryResponse{ - MemoryIds: make([]string, 0), - Status: "Success", - } + var memoryIds []string session := service.service.session.(*Session) response := session.query(HTTPOperation.GET, session.redfishPaths[SystemMemoryChunksKey]) if response.err != nil { - memoryResponse.Status = "Failure" - memoryResponse.ServiceError = response.err - - return &memoryResponse, response.err + newErr := response.err + return &GetMemoryResponse{Status: "Failure"}, newErr } members, _ := response.arrayFromJSON("Members") @@ -1839,7 +1779,7 @@ func (service *httpfishService) GetMemory(ctx context.Context, settings *Configu components := strings.Split(uri, "/") if len(components) > 0 { - memoryResponse.MemoryIds = append(memoryResponse.MemoryIds, components[len(components)-1]) + memoryIds = append(memoryIds, components[len(components)-1]) session.memoryChunkPath[components[len(components)-1]] = uri } } @@ -1850,10 +1790,8 @@ func (service *httpfishService) GetMemory(ctx context.Context, settings *Configu response = session.query(HTTPOperation.GET, cxlMemoryPath) if response.err != nil { - memoryResponse.Status = "Failure" - memoryResponse.ServiceError = response.err - - return &memoryResponse, response.err + newErr := response.err + return &GetMemoryResponse{Status: "Failure"}, newErr } members, _ = response.arrayFromJSON("Members") @@ -1864,13 +1802,13 @@ func (service *httpfishService) GetMemory(ctx context.Context, settings *Configu components := strings.Split(uri, "/") if len(components) > 0 { - memoryResponse.MemoryIds = append(memoryResponse.MemoryIds, components[len(components)-1]) + memoryIds = append(memoryIds, components[len(components)-1]) session.memoryChunkPath[components[len(components)-1]] = uri } } } - return &memoryResponse, nil + return &GetMemoryResponse{MemoryIds: memoryIds, Status: "Success"}, nil } // GetBackendInfo: Get the information of this backend diff --git a/pkg/backend/ops.go b/pkg/backend/ops.go index aab9d6b..e2b662f 100644 --- a/pkg/backend/ops.go +++ b/pkg/backend/ops.go @@ -12,32 +12,29 @@ type CreateSessionRequest struct { } type CreateSessionResponse struct { - SessionId string // The session id returned form creating a session - Status string // The status of the request - ServiceError error // Any error returned by the service - ChassisSN string // The serial number returned from the redfish chassis schema - EnclosureSN string // The serial number returned from the redfish chassis schema for the enclosure + SessionId string // The session id returned form creating a session + Status string // The status of the request + ChassisSN string // The serial number returned from the redfish chassis schema + EnclosureSN string // The serial number returned from the redfish chassis schema for the enclosure } type DeleteSessionRequest struct { } type DeleteSessionResponse struct { - SessionId string // The session id we are ending - IpAddress string // Ip Address - Port int32 // Port - Status string // The status of the request - ServiceError error // Any error returned by the service + SessionId string // The session id we are ending + IpAddress string // Ip Address + Port int32 // Port + Status string // The status of the request } type PortNumber int32 type GetMemoryRegionResponse struct { - Id string // Id of the memory region - Ports []PortNumber // Port numbers on fabric - CapacityMiB int32 // Allocated memory capacity - Status string // The status of the request - ServiceError error // Any error returned by the service + Id string // Id of the memory region + Ports []PortNumber // Port numbers on fabric + CapacityMiB int32 // Allocated memory capacity + Status string // The status of the request } type QoS int32 @@ -48,10 +45,9 @@ type AllocateMemoryRequest struct { } type AllocateMemoryResponse struct { - SizeMiB int32 // The allocated number of mebibytes (This may be adjusted to implement dimm interleave) - MemoryId string // The id of the memory region allocated\provisioned - Status string // The status of the request - ServiceError error // Any error returned by the service + SizeMiB int32 // The allocated number of mebibytes (This may be adjusted to implement dimm interleave) + MemoryId string // The id of the memory region allocated\provisioned + Status string // The status of the request } type AllocateMemoryByResourceRequest struct { @@ -59,10 +55,9 @@ type AllocateMemoryByResourceRequest struct { } type AllocateMemoryByResourceResponse struct { - SizeMiB int32 // The allocated number of mebibytes - MemoryId string // The id of the memory region allocated\provisioned - Status string // The status of the request - ServiceError error // Any error returned by the service + SizeMiB int32 // The allocated number of mebibytes + MemoryId string // The id of the memory region allocated\provisioned + Status string // The status of the request } type AssignMemoryRequest struct { @@ -71,8 +66,7 @@ type AssignMemoryRequest struct { } type AssignMemoryResponse struct { - Status string // The status of the request - ServiceError error // Any error returned by the service + Status string // The status of the request } type UnassignMemoryRequest struct { @@ -81,8 +75,7 @@ type UnassignMemoryRequest struct { } type UnassignMemoryResponse struct { - Status string // The status of the request - ServiceError error // Any error returned by the service + Status string // The status of the request } type MemoryResourceBlocksRequest struct { @@ -91,16 +84,14 @@ type MemoryResourceBlocksRequest struct { type MemoryResourceBlocksResponse struct { MemoryResources []string // Array to hold ids of memory resources, get from memory appliance Status string // The status of the request - ServiceError error // Any error returned by the service } type GetPortsRequest struct { } type GetPortsResponse struct { - PortIds []string // Array to hold ids of ports - Status string // The status of the request - ServiceError error // Any error returned by the service + PortIds []string // Array to hold ids of ports + Status string // The status of the request } type GetPortDetailsRequest struct { @@ -124,7 +115,6 @@ type PortInformation struct { type GetPortDetailsResponse struct { PortInformation PortInformation // Detail info for one port id Status string // The status of the request - ServiceError error // Any error returned by the service } type GetHostPortSnByIdRequest struct { @@ -134,16 +124,14 @@ type GetHostPortSnByIdRequest struct { type GetHostPortSnByIdResponse struct { SerialNumber string Status string // The status of the request - ServiceError error // Any error returned by the service } type GetMemoryDevicesRequest struct { } type GetMemoryDevicesResponse struct { - DeviceIdMap map[string][]string // map of physical devices ids (keys) to 1 or more logical device ids (values) - Status string // The status of the request - ServiceError error // Any error returned by the service + DeviceIdMap map[string][]string // map of physical devices ids (keys) to 1 or more logical device ids (values) + Status string // The status of the request } type GetMemoryDeviceDetailsRequest struct { @@ -164,7 +152,6 @@ type GetMemoryDeviceDetailsResponse struct { MemorySizeMiB int32 // The memory size of the device in MiB LinkStatus *MemoryDeviceLinkStatus // Detail info for one port id Status string // The status of the request - ServiceError error // Any error returned by the service } type MemoryResourceBlockByIdRequest struct { @@ -222,7 +209,6 @@ type MemoryResourceBlock struct { type MemoryResourceBlockByIdResponse struct { MemoryResourceBlock MemoryResourceBlock // Detail info for one memory resource block Status string // The status of the request - ServiceError error // Any error returned by the service } type FreeMemoryRequest struct { @@ -230,8 +216,7 @@ type FreeMemoryRequest struct { } type FreeMemoryResponse struct { - Status string // The status of the request - ServiceError error // Any error returned by the service + Status string // The status of the request } type GetMemoryByIdRequest struct { @@ -262,16 +247,14 @@ type TypeMemoryRegion struct { type GetMemoryByIdResponse struct { MemoryRegion TypeMemoryRegion Status string // The status of the request - ServiceError error // Any error returned by the service } type GetMemoryRequest struct { } type GetMemoryResponse struct { - MemoryIds []string // list of memory ids - Status string // The status of the request - ServiceError error // Any error returned by the service + MemoryIds []string // list of memory ids + Status string // The status of the request } type GetBackendInfoResponse struct { diff --git a/pkg/manager/blade.go b/pkg/manager/blade.go index fbfb1ea..32753ad 100644 --- a/pkg/manager/blade.go +++ b/pkg/manager/blade.go @@ -917,7 +917,7 @@ func (b *Blade) initMemory(ctx context.Context) error { logger.V(4).Info(">>>>>> initMemory: ", "bladeId", b.Id, "applianceId", b.ApplianceId) memoryIds, err := b.GetMemoryBackend(ctx) - if err != nil || memoryIds == nil { + if err != nil { newErr := fmt.Errorf("blade [%s] init failed during get memory: %w", b.Id, err) logger.Error(newErr, "failure: init memory: blade") return newErr From e4221dec4c1126093ad902fac670a349f2bfde6a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 18 Sep 2024 12:57:32 -0600 Subject: [PATCH 04/12] build(deps): bump axios from 1.7.2 to 1.7.4 in /webui (#32) Bumps [axios](https://github.com/axios/axios) from 1.7.2 to 1.7.4. - [Release notes](https://github.com/axios/axios/releases) - [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md) - [Commits](https://github.com/axios/axios/compare/v1.7.2...v1.7.4) --- updated-dependencies: - dependency-name: axios dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- webui/package-lock.json | 8 ++++---- webui/package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/webui/package-lock.json b/webui/package-lock.json index e82a947..d00f3bc 100644 --- a/webui/package-lock.json +++ b/webui/package-lock.json @@ -12,7 +12,7 @@ "@mdi/font": "7.0.96", "@vue-flow/controls": "^1.1.2", "@vue-flow/core": "^1.39.0", - "axios": "^1.7.2", + "axios": "^1.7.4", "chart.js": "^4.4.3", "core-js": "^3.29.0", "d3-force": "^3.0.0", @@ -1227,9 +1227,9 @@ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, "node_modules/axios": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.2.tgz", - "integrity": "sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==", + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.4.tgz", + "integrity": "sha512-DukmaFRnY6AzAALSH4J2M3k6PkaC+MfaAGdEERRWcC9q3/TWQwLpHR8ZRLKTdQ3aBDL64EdluRDjJqKw+BPZEw==", "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.0", diff --git a/webui/package.json b/webui/package.json index 990f611..8384946 100644 --- a/webui/package.json +++ b/webui/package.json @@ -13,7 +13,7 @@ "@mdi/font": "7.0.96", "@vue-flow/controls": "^1.1.2", "@vue-flow/core": "^1.39.0", - "axios": "^1.7.2", + "axios": "^1.7.4", "chart.js": "^4.4.3", "core-js": "^3.29.0", "d3-force": "^3.0.0", From 94fbf3e932609220d2ae233276fa537553ec3ce4 Mon Sep 17 00:00:00 2001 From: Scott Howe Date: Tue, 24 Sep 2024 10:47:16 -0600 Subject: [PATCH 05/12] feat: add online\offline status to manager and datastore (#29) Added a new backend api to perform a simple query of the redfish root service for a blade\host device. This allows for a quick check to see if the device, after previously being successfully added to the service, is still present. This quick check translates directly to an online\offline status for the device. This is mainly used to handle the situation when a blade\host get powered down. This status is then used to modify the service's behavior for that device. The status state is also returned via the client so that the webui\cli can display useful information to the user about the device's current state. --- pkg/api/api_default_service.go | 237 ++++++++++++++---------------- pkg/api/api_redfish_service.go | 14 +- pkg/backend/backend.go | 1 + pkg/backend/httpfish.go | 15 ++ pkg/backend/ops.go | 7 + pkg/common/common.go | 9 ++ pkg/common/datastore/datastore.go | 163 +++++++++++--------- pkg/common/status.go | 8 + pkg/manager/appliance.go | 74 ++++++---- pkg/manager/blade.go | 117 ++++++++++++--- pkg/manager/host.go | 114 +++++++++++--- pkg/manager/manager.go | 120 ++++++++------- pkg/manager/port.go | 2 +- 13 files changed, 549 insertions(+), 332 deletions(-) create mode 100644 pkg/common/common.go diff --git a/pkg/api/api_default_service.go b/pkg/api/api_default_service.go index 5d489e7..e58031f 100644 --- a/pkg/api/api_default_service.go +++ b/pkg/api/api_default_service.go @@ -17,7 +17,6 @@ import ( "sort" "cfm/pkg/common" - "cfm/pkg/common/datastore" "cfm/pkg/manager" "cfm/pkg/openapi" ) @@ -42,25 +41,25 @@ func NewCfmApiService(version string) openapi.DefaultAPIServicer { // AppliancesDeleteById - func (cfm *CfmApiService) AppliancesDeleteById(ctx context.Context, applianceId string) (openapi.ImplResponse, error) { + var a openapi.Appliance + appliance, err := manager.DeleteApplianceById(ctx, applianceId) if err != nil { - return formatErrorResp(ctx, err.(*common.RequestError)) - } + a = openapi.Appliance{Id: applianceId} - // Update DataStore. - datastore.DStore().GetDataStore().DeleteAppliance(appliance.Id) - datastore.DStore().Store() + } else { - a := openapi.Appliance{ - Id: appliance.Id, - IpAddress: "", // Unused (May need for POC4) - Port: 0, // Unused (May need for POC4) - Status: "", // Unused - Blades: openapi.MemberItem{ - Uri: manager.GetCfmUriBlades(appliance.Id), - }, - TotalMemoryAvailableMiB: -1, // Not Implemented - TotalMemoryAllocatedMiB: -1, // Not Implemented + a = openapi.Appliance{ + Id: appliance.Id, + IpAddress: "", // Unused (May need for POC4) + Port: 0, // Unused (May need for POC4) + Status: "", // Unused + Blades: openapi.MemberItem{ + Uri: manager.GetCfmUriBlades(appliance.Id), + }, + TotalMemoryAvailableMiB: -1, // Not Implemented + TotalMemoryAllocatedMiB: -1, // Not Implemented + } } return openapi.Response(http.StatusOK, a), nil @@ -129,12 +128,6 @@ func (cfm *CfmApiService) AppliancesPost(ctx context.Context, credentials openap return formatErrorResp(ctx, err.(*common.RequestError)) } - // The user's purpose for "credentials" object is complete. Now, reusing this object for DataStore. - // Update credentials ID with final appliance ID. - credentials.CustomId = appliance.Id - datastore.DStore().GetDataStore().AddAppliance(&credentials) - datastore.DStore().Store() - a := openapi.Appliance{ Id: appliance.Id, IpAddress: "", // Unused @@ -152,13 +145,7 @@ func (cfm *CfmApiService) AppliancesPost(ctx context.Context, credentials openap // AppliancesResync - func (cfm *CfmApiService) AppliancesResyncById(ctx context.Context, applianceId string) (openapi.ImplResponse, error) { - appliance, failedBladeIds, err := manager.ResyncApplianceById(ctx, applianceId) - - for _, id := range *failedBladeIds { - // Update DataStore. - datastore.DStore().GetDataStore().DeleteBlade(id, applianceId) - datastore.DStore().Store() - } + appliance, err := manager.ResyncApplianceById(ctx, applianceId) if err != nil { if appliance != nil { @@ -268,8 +255,10 @@ func (cfm *CfmApiService) BladesComposeMemoryByResource(ctx context.Context, app return openapi.Response(http.StatusCreated, memory), nil } -// BladesDeleteById - +// BladesDeleteById - As long as the appliance id is valid, guarenteed blade deletion from service func (cfm *CfmApiService) BladesDeleteById(ctx context.Context, applianceId string, bladeId string) (openapi.ImplResponse, error) { + var b openapi.Blade + appliance, err := manager.GetApplianceById(ctx, applianceId) if err != nil { return formatErrorResp(ctx, err.(*common.RequestError)) @@ -277,29 +266,27 @@ func (cfm *CfmApiService) BladesDeleteById(ctx context.Context, applianceId stri blade, err := appliance.DeleteBladeById(ctx, bladeId) if err != nil { - return formatErrorResp(ctx, err.(*common.RequestError)) - } - - // Update DataStore. - datastore.DStore().GetDataStore().DeleteBlade(blade.Id, applianceId) - datastore.DStore().Store() - - b := openapi.Blade{ - Id: blade.Id, - IpAddress: blade.GetNetIp(), - Port: int32(blade.GetNetPort()), - Status: "", // Unused - Ports: openapi.MemberItem{ - Uri: manager.GetCfmUriBladePorts(appliance.Id, blade.Id), - }, - Resources: openapi.MemberItem{ - Uri: manager.GetCfmUriBladeResources(appliance.Id, blade.Id), - }, - Memory: openapi.MemberItem{ - Uri: manager.GetCfmUriBladeMemory(appliance.Id, blade.Id), - }, - TotalMemoryAvailableMiB: -1, // Not Implemented - TotalMemoryAllocatedMiB: -1, // Not Implemented + b = openapi.Blade{Id: bladeId} + + } else { + + b = openapi.Blade{ + Id: blade.Id, + IpAddress: blade.GetNetIp(), + Port: int32(blade.GetNetPort()), + Status: string(blade.Status), + Ports: openapi.MemberItem{ + Uri: manager.GetCfmUriBladePorts(appliance.Id, blade.Id), + }, + Resources: openapi.MemberItem{ + Uri: manager.GetCfmUriBladeResources(appliance.Id, blade.Id), + }, + Memory: openapi.MemberItem{ + Uri: manager.GetCfmUriBladeMemory(appliance.Id, blade.Id), + }, + TotalMemoryAvailableMiB: -1, // Not Implemented + TotalMemoryAllocatedMiB: -1, // Not Implemented + } } return openapi.Response(http.StatusOK, b), nil @@ -371,7 +358,7 @@ func (cfm *CfmApiService) BladesGetById(ctx context.Context, applianceId string, Id: blade.Id, IpAddress: blade.GetNetIp(), Port: int32(blade.GetNetPort()), - Status: "", // Unused + Status: string(blade.Status), Ports: openapi.MemberItem{ Uri: manager.GetCfmUriBladePorts(appliance.Id, blade.Id), }, @@ -401,7 +388,7 @@ func (cfm *CfmApiService) BladesGetMemory(ctx context.Context, applianceId strin } // order returned uris by memory id - memoryIds := blade.GetAllMemoryIds() + memoryIds := blade.GetAllMemoryIds(ctx) sort.Strings(memoryIds) memory := blade.GetMemory(ctx) @@ -435,12 +422,17 @@ func (cfm *CfmApiService) BladesGetMemoryById(ctx context.Context, applianceId s return formatErrorResp(ctx, err.(*common.RequestError)) } - details, err := memory.GetDetails(ctx) - if err != nil { - return formatErrorResp(ctx, err.(*common.RequestError)) - } + if memory != nil { + details, err := memory.GetDetails(ctx) + if err != nil { + return formatErrorResp(ctx, err.(*common.RequestError)) + } - return openapi.Response(http.StatusOK, details), nil + return openapi.Response(http.StatusOK, details), nil + + } else { + return openapi.Response(http.StatusOK, openapi.MemoryRegion{}), nil + } } // BladesGetPortById - @@ -460,12 +452,17 @@ func (cfm *CfmApiService) BladesGetPortById(ctx context.Context, applianceId str return formatErrorResp(ctx, err.(*common.RequestError)) } - details, err := port.GetDetails(ctx) - if err != nil { - return formatErrorResp(ctx, err.(*common.RequestError)) - } + if port != nil { + details, err := port.GetDetails(ctx) + if err != nil { + return formatErrorResp(ctx, err.(*common.RequestError)) + } - return openapi.Response(http.StatusOK, details), nil + return openapi.Response(http.StatusOK, details), nil + + } else { + return openapi.Response(http.StatusOK, openapi.PortInformation{}), nil + } } // BladesGetPorts - @@ -481,7 +478,7 @@ func (cfm *CfmApiService) BladesGetPorts(ctx context.Context, applianceId string } // order returned uris by port id - portIds := blade.GetAllPortIds() + portIds := blade.GetAllPortIds(ctx) sort.Strings(portIds) ports := blade.GetPorts(ctx) @@ -515,12 +512,17 @@ func (cfm *CfmApiService) BladesGetResourceById(ctx context.Context, applianceId return formatErrorResp(ctx, err.(*common.RequestError)) } - details, err := resource.GetDetails(ctx) - if err != nil { - return formatErrorResp(ctx, err.(*common.RequestError)) - } + if resource != nil { + details, err := resource.GetDetails(ctx) + if err != nil { + return formatErrorResp(ctx, err.(*common.RequestError)) + } - return openapi.Response(http.StatusOK, details), nil + return openapi.Response(http.StatusOK, details), nil + + } else { + return openapi.Response(http.StatusOK, openapi.MemoryResourceBlock{}), nil + } } // BladesGetResources - @@ -536,7 +538,7 @@ func (cfm *CfmApiService) BladesGetResources(ctx context.Context, applianceId st } // order returned uris by resourse id - resourceIds := blade.GetAllResourceIds() + resourceIds := blade.GetAllResourceIds(ctx) sort.Strings(resourceIds) resources := blade.GetResources(ctx) @@ -560,11 +562,10 @@ func (cfm *CfmApiService) BladesPost(ctx context.Context, applianceId string, cr return formatErrorResp(ctx, err.(*common.RequestError)) } - // Appliance can be empty but may include up to 8 blades if len(appliance.Blades) == MAX_COUNT_BLADES { err := common.RequestError{ StatusCode: common.StatusBladesExceedMaximum, - Err: fmt.Errorf("no more blades can be associated with this appliance (%s)", applianceId), + Err: fmt.Errorf("cfm-service at maximum blade capacity (%d) for this appliance (%s)", MAX_COUNT_BLADES, applianceId), } return formatErrorResp(ctx, &err) } @@ -574,12 +575,6 @@ func (cfm *CfmApiService) BladesPost(ctx context.Context, applianceId string, cr return formatErrorResp(ctx, err.(*common.RequestError)) } - // The user's purpose for "credentials" object is complete. Now, reusing this object for DataStore. - // Update credentials ID with final blade ID. - credentials.CustomId = blade.Id - datastore.DStore().GetDataStore().AddBlade(&credentials, appliance.Id) - datastore.DStore().Store() - totals, err := blade.GetResourceTotals(ctx) if err != nil { return formatErrorResp(ctx, err.(*common.RequestError)) @@ -589,7 +584,7 @@ func (cfm *CfmApiService) BladesPost(ctx context.Context, applianceId string, cr Id: blade.Id, IpAddress: blade.GetNetIp(), Port: int32(blade.GetNetPort()), - Status: "", // Unused + Status: string(blade.Status), Ports: openapi.MemberItem{ Uri: manager.GetCfmUriBladePorts(appliance.Id, blade.Id), }, @@ -615,10 +610,6 @@ func (cfm *CfmApiService) BladesResyncById(ctx context.Context, applianceId stri blade, err := appliance.ResyncBladeById(ctx, bladeId) if err != nil { - // Update DataStore. - datastore.DStore().GetDataStore().DeleteBlade(blade.Id, applianceId) - datastore.DStore().Store() - return formatErrorResp(ctx, err.(*common.RequestError)) } @@ -631,7 +622,7 @@ func (cfm *CfmApiService) BladesResyncById(ctx context.Context, applianceId stri Id: blade.Id, IpAddress: blade.GetNetIp(), Port: int32(blade.GetNetPort()), - Status: "", // Unused + Status: string(blade.Status), Ports: openapi.MemberItem{ Uri: manager.GetCfmUriBladePorts(appliance.Id, blade.Id), }, @@ -656,7 +647,7 @@ func (cfm *CfmApiService) HostGetMemory(ctx context.Context, hostId string) (ope } // order returned uris by memory id - memoryIds := host.GetAllMemoryIds() + memoryIds := host.GetAllMemoryIds(ctx) sort.Strings(memoryIds) memory := host.GetMemory(ctx) @@ -694,33 +685,33 @@ func (cfm *CfmApiService) HostsComposeMemory(ctx context.Context, hostId string, return openapi.Response(http.StatusCreated, memory), nil } -// HostsDeleteById - +// HostsDeleteById - Guarenteed host deletion from service. func (cfm *CfmApiService) HostsDeleteById(ctx context.Context, hostId string) (openapi.ImplResponse, error) { - host, err := manager.DeleteHostById(ctx, hostId) - if err != nil { - return formatErrorResp(ctx, err.(*common.RequestError)) - } + var h openapi.Host - // Update DataStore. - datastore.DStore().GetDataStore().DeleteHost(host.Id) - datastore.DStore().Store() - - h := openapi.Host{ - Id: host.Id, - IpAddress: host.GetNetIp(), - Port: int32(host.GetNetPort()), - Status: "", // Unused - Ports: openapi.MemberItem{ - Uri: manager.GetCfmUriHostPorts(host.Id), - }, - Memory: openapi.MemberItem{ - Uri: manager.GetCfmUriHostMemory(host.Id), - }, - MemoryDevices: openapi.MemberItem{ - Uri: manager.GetCfmUriHostMemoryDevices(host.Id), - }, - LocalMemoryMiB: -1, // Not implemented - RemoteMemoryMiB: -1, // Not implemented + host, err := manager.DeleteHostById(ctx, hostId) + if err != nil && host == nil { + h = openapi.Host{Id: hostId} + + } else { + + h = openapi.Host{ + Id: host.Id, + IpAddress: host.GetNetIp(), + Port: int32(host.GetNetPort()), + Status: string(host.Status), + Ports: openapi.MemberItem{ + Uri: manager.GetCfmUriHostPorts(host.Id), + }, + Memory: openapi.MemberItem{ + Uri: manager.GetCfmUriHostMemory(host.Id), + }, + MemoryDevices: openapi.MemberItem{ + Uri: manager.GetCfmUriHostMemoryDevices(host.Id), + }, + LocalMemoryMiB: -1, // Not implemented + RemoteMemoryMiB: -1, // Not implemented + } } return openapi.Response(http.StatusOK, h), nil @@ -764,7 +755,7 @@ func (cfm *CfmApiService) HostsGet(ctx context.Context) (openapi.ImplResponse, e // HostsGetById - Get information for a single CXL Host. func (cfm *CfmApiService) HostsGetById(ctx context.Context, hostId string) (openapi.ImplResponse, error) { host, err := manager.GetHostById(ctx, hostId) - if err != nil { + if err != nil || host == nil { return formatErrorResp(ctx, err.(*common.RequestError)) } @@ -777,7 +768,7 @@ func (cfm *CfmApiService) HostsGetById(ctx context.Context, hostId string) (open Id: host.Id, IpAddress: host.GetNetIp(), Port: int32(host.GetNetPort()), - Status: "", // Unused + Status: string(host.Status), Ports: openapi.MemberItem{ Uri: manager.GetCfmUriHostPorts(host.Id), }, @@ -842,7 +833,7 @@ func (cfm *CfmApiService) HostsGetMemoryDevices(ctx context.Context, hostId stri } // order returned uris by memory device id - memdevIds := host.GetAllMemoryDeviceIds() + memdevIds := host.GetAllMemoryDeviceIds(ctx) sort.Strings(memdevIds) memdevs := host.GetMemoryDevices(ctx) @@ -887,7 +878,7 @@ func (cfm *CfmApiService) HostsGetPorts(ctx context.Context, hostId string) (ope } // order returned uris by port id - portIds := host.GetAllPortIds() + portIds := host.GetAllPortIds(ctx) sort.Strings(portIds) ports := host.GetPorts(ctx) @@ -920,17 +911,11 @@ func (cfm *CfmApiService) HostsPost(ctx context.Context, credentials openapi.Cre return formatErrorResp(ctx, err.(*common.RequestError)) } - // The user's purpose for "credentials" object is complete. Now, reusing this object for DataStore. - // Update credentials ID with final host ID. - credentials.CustomId = host.Id - datastore.DStore().GetDataStore().AddHost(&credentials) - datastore.DStore().Store() - h := openapi.Host{ Id: host.Id, IpAddress: host.GetNetIp(), Port: int32(host.GetNetPort()), - Status: "", // Unused + Status: string(host.Status), Ports: openapi.MemberItem{ Uri: manager.GetCfmUriHostPorts(host.Id), }, @@ -951,10 +936,6 @@ func (cfm *CfmApiService) HostsPost(ctx context.Context, credentials openapi.Cre func (cfm *CfmApiService) HostsResyncById(ctx context.Context, hostId string) (openapi.ImplResponse, error) { host, err := manager.ResyncHostById(ctx, hostId) if err != nil { - // Update DataStore. - datastore.DStore().GetDataStore().DeleteHost(host.Id) - datastore.DStore().Store() - return formatErrorResp(ctx, err.(*common.RequestError)) } @@ -962,7 +943,7 @@ func (cfm *CfmApiService) HostsResyncById(ctx context.Context, hostId string) (o Id: host.Id, IpAddress: host.GetNetIp(), Port: int32(host.GetNetPort()), - Status: "", // Unused + Status: string(host.Status), Ports: openapi.MemberItem{ Uri: manager.GetCfmUriHostPorts(host.Id), }, diff --git a/pkg/api/api_redfish_service.go b/pkg/api/api_redfish_service.go index ccce4b1..7a74513 100644 --- a/pkg/api/api_redfish_service.go +++ b/pkg/api/api_redfish_service.go @@ -287,7 +287,7 @@ func (cfm *CfmApiService) RedfishV1ChassisChassisIdPCIeDevicesGet(ctx context.Co } // order returned uris by memory device id - memdevIds := host.GetAllMemoryDeviceIds() + memdevIds := host.GetAllMemoryDeviceIds(ctx) sort.Strings(memdevIds) members := []redfishapi.OdataV4IdRef{} @@ -588,7 +588,7 @@ func (cfm *CfmApiService) RedfishV1FabricsFabricIdConnectionsGet(ctx context.Con if err != nil { return formatRedfishErrorResp(ctx, err.(*common.RequestError)) } - memoryIds := blade.GetAllMemoryIds() + memoryIds := blade.GetAllMemoryIds(ctx) sort.Strings(memoryIds) for _, memId := range memoryIds { @@ -652,7 +652,7 @@ func (cfm *CfmApiService) RedfishV1FabricsFabricIdConnectionsPost(ctx context.Co endpointOdataId := connectionV131Connection.Links.TargetEndpoints[0].OdataId for bladeId, blade := range appliance.GetBlades(ctx) { - for _, memoryId := range blade.GetAllMemoryIds() { + for _, memoryId := range blade.GetAllMemoryIds(ctx) { if fmt.Sprintf("/redfish/v1/Systems/%s/MemoryDomains/%s/MemoryChunks/%s", fabricId, bladeId, memoryId) == memoryChunkOdataId { targetBladeId = bladeId r.MemoryId = memoryId @@ -662,7 +662,7 @@ func (cfm *CfmApiService) RedfishV1FabricsFabricIdConnectionsPost(ctx context.Co if r.MemoryId == "" { continue } - for _, portid := range blade.GetAllPortIds() { + for _, portid := range blade.GetAllPortIds(ctx) { if fmt.Sprintf("/redfish/v1/Fabrics/%s/Endpoints/%s", fabricId, bladeId+strings.Replace(portid, "port", "up", 1)) == endpointOdataId { r.PortId = portid break @@ -784,7 +784,7 @@ func (cfm *CfmApiService) RedfishV1FabricsFabricIdEndpointsGet(ctx context.Conte members := []redfishapi.OdataV4IdRef{} for _, bladeId := range bladeIds { blade, _ := appliance.GetBladeById(ctx, bladeId) - for _, portid := range blade.GetAllPortIds() { + for _, portid := range blade.GetAllPortIds(ctx) { members = append(members, redfishapi.OdataV4IdRef{ OdataId: fmt.Sprintf("/redfish/v1/Fabrics/%s/Endpoints/%s", fabricId, bladeId+strings.Replace(portid, "port", "up", 1))}) } @@ -943,7 +943,7 @@ func (cfm *CfmApiService) RedfishV1FabricsFabricIdSwitchesSwitchIdPortsGet(ctx c } // order returned uris by port id - portIds := blade.GetAllPortIds() + portIds := blade.GetAllPortIds(ctx) sort.Strings(portIds) members := []redfishapi.OdataV4IdRef{} for _, member := range portIds { @@ -1377,7 +1377,7 @@ func (cfm *CfmApiService) RedfishV1SystemsComputerSystemIdMemoryDomainsMemoryDom } // order returned uris by memory id - memoryIds := blade.GetAllMemoryIds() + memoryIds := blade.GetAllMemoryIds(ctx) sort.Strings(memoryIds) members := []redfishapi.OdataV4IdRef{} for _, member := range memoryIds { diff --git a/pkg/backend/backend.go b/pkg/backend/backend.go index 2732282..65b89dd 100644 --- a/pkg/backend/backend.go +++ b/pkg/backend/backend.go @@ -10,6 +10,7 @@ import ( type BackendOperations interface { CreateSession(context.Context, *ConfigurationSettings, *CreateSessionRequest) (*CreateSessionResponse, error) DeleteSession(context.Context, *ConfigurationSettings, *DeleteSessionRequest) (*DeleteSessionResponse, error) + GetRootService(context.Context, *ConfigurationSettings, *GetRootServiceRequest) (*GetRootServiceResponse, error) GetMemoryResourceBlocks(context.Context, *ConfigurationSettings, *MemoryResourceBlocksRequest) (*MemoryResourceBlocksResponse, error) GetMemoryResourceBlockById(context.Context, *ConfigurationSettings, *MemoryResourceBlockByIdRequest) (*MemoryResourceBlockByIdResponse, error) GetPorts(context.Context, *ConfigurationSettings, *GetPortsRequest) (*GetPortsResponse, error) diff --git a/pkg/backend/httpfish.go b/pkg/backend/httpfish.go index 0ce0ab5..45e3dff 100644 --- a/pkg/backend/httpfish.go +++ b/pkg/backend/httpfish.go @@ -490,6 +490,21 @@ func (session *Session) auth() error { return response.err } +// GetRootService: Retrieve root service from endpoint +func (service *httpfishService) GetRootService(ctx context.Context, settings *ConfigurationSettings, req *GetRootServiceRequest) (*GetRootServiceResponse, error) { + session := service.service.session.(*Session) + + response := session.query(HTTPOperation.GET, redfish_serviceroot) + if response.err != nil { + return nil, fmt.Errorf("failed to get root service: %w", response.err) + } + + name, _ := response.stringFromJSON("Name") + uuid, _ := response.stringFromJSON("UUID") + + return &GetRootServiceResponse{Name: name, Uuid: uuid}, nil +} + // CreateSession: Create a new session with an endpoint service func (service *httpfishService) CreateSession(ctx context.Context, settings *ConfigurationSettings, req *CreateSessionRequest) (*CreateSessionResponse, error) { logger := klog.FromContext(ctx) diff --git a/pkg/backend/ops.go b/pkg/backend/ops.go index e2b662f..86e6098 100644 --- a/pkg/backend/ops.go +++ b/pkg/backend/ops.go @@ -262,3 +262,10 @@ type GetBackendInfoResponse struct { Version string SessionId string } +type GetRootServiceRequest struct { +} + +type GetRootServiceResponse struct { + Name string + Uuid string +} diff --git a/pkg/common/common.go b/pkg/common/common.go new file mode 100644 index 0000000..cad91de --- /dev/null +++ b/pkg/common/common.go @@ -0,0 +1,9 @@ +package common + +type ConnectionStatus string + +const ( + ONLINE ConnectionStatus = "online" + OFFLINE ConnectionStatus = "offline" + NOT_APPLICABLE ConnectionStatus = "n\\a" +) diff --git a/pkg/common/datastore/datastore.go b/pkg/common/datastore/datastore.go index 3124926..18affdc 100644 --- a/pkg/common/datastore/datastore.go +++ b/pkg/common/datastore/datastore.go @@ -6,6 +6,7 @@ import ( "context" "fmt" + "cfm/pkg/common" "cfm/pkg/openapi" "k8s.io/klog/v2" @@ -16,59 +17,55 @@ const ( ) type DataStore struct { - SavedAppliances map[string]*ApplianceDataStore `json:"saved-appliances"` - SavedHosts map[string]*HostDataStore `json:"saved-hosts"` + ApplianceData map[string]*ApplianceDatum `json:"appliance-data"` + HostData map[string]*HostDatum `json:"host-data"` } func NewDataStore() *DataStore { return &DataStore{ - SavedAppliances: make(map[string]*ApplianceDataStore), - SavedHosts: make(map[string]*HostDataStore), + ApplianceData: make(map[string]*ApplianceDatum), + HostData: make(map[string]*HostDatum), } } -// AddAppliance: Add a new appliance to the data store -func (c *DataStore) AddAppliance(creds *openapi.Credentials) { - c.SavedAppliances[creds.CustomId] = NewApplianceDataStore(creds) +// AddApplianceDatum: Add a new appliance datum to the data store +func (c *DataStore) AddApplianceDatum(creds *openapi.Credentials) { + c.ApplianceData[creds.CustomId] = NewApplianceDatum(creds) } -// AddBlade: Add a new blade to the data store -func (c *DataStore) AddBlade(creds *openapi.Credentials, applianceId string) error { - appliance, exists := c.SavedAppliances[applianceId] - if !exists { - return fmt.Errorf("appliance [%s] not found in data store add", applianceId) - } - - appliance.AddBlade(creds) - - return nil +// AddHostDatum: Add a new host datum to the data store +func (c *DataStore) AddHostDatum(creds *openapi.Credentials) { + c.HostData[creds.CustomId] = NewHostDatum(creds) } -// AddHost: Add a new host to the data store -func (c *DataStore) AddHost(creds *openapi.Credentials) { - c.SavedHosts[creds.CustomId] = NewHostDataStore(creds) +// DeleteApplianceDatumById: Delete an appliance from the data store +func (c *DataStore) DeleteApplianceDatumById(applianceId string) { + delete(c.ApplianceData, applianceId) } -// DeleteAppliance: Delete an appliance from the data store -func (c *DataStore) DeleteAppliance(applianceId string) { - delete(c.SavedAppliances, applianceId) +// DeleteHostDatumById: Delete a host datum from the data store +func (c *DataStore) DeleteHostDatumById(hostId string) { + delete(c.HostData, hostId) } -// DeleteBlade: Delete a blade from an appliance's data store -func (c *DataStore) DeleteBlade(bladeId, applianceId string) error { - appliance, exists := c.SavedAppliances[applianceId] +// GetApplianceDatumById: Retrieve an appliance datum from the data store +func (c *DataStore) GetApplianceDatumById(applianceId string) (*ApplianceDatum, error) { + datum, exists := c.ApplianceData[applianceId] if !exists { - return fmt.Errorf("appliance [%s] not found in data store delete", applianceId) + return nil, fmt.Errorf("appliance datum [%s] not found in data store", applianceId) } - appliance.DeleteBlade(bladeId) - - return nil + return datum, nil } -// DeleteHost: Delete an host from the data store -func (c *DataStore) DeleteHost(hostId string) { - delete(c.SavedHosts, hostId) +// GetHostDatumById: Retrieve a host datum from the data store +func (c *DataStore) GetHostDatumById(hostId string) (*HostDatum, error) { + datum, exists := c.HostData[hostId] + if !exists { + return nil, fmt.Errorf("host datum [%s] not found in data store", hostId) + } + + return datum, nil } // Init: initialize the data store using command line args, ENV, or a file @@ -79,46 +76,77 @@ func (c *DataStore) InitDataStore(ctx context.Context, args []string) error { return nil } -type ApplianceDataStore struct { - Credentials *openapi.Credentials `json:"credentials"` - SavedBlades map[string]*BladeDataStore `json:"saved-blades"` +type ApplianceDatum struct { + Credentials *openapi.Credentials `json:"credentials"` + BladeData map[string]*BladeDatum `json:"blade-data"` + ConnectionStatus common.ConnectionStatus `json:"connection-status"` } -func NewApplianceDataStore(creds *openapi.Credentials) *ApplianceDataStore { - return &ApplianceDataStore{ - Credentials: creds, - SavedBlades: make(map[string]*BladeDataStore), +func NewApplianceDatum(creds *openapi.Credentials) *ApplianceDatum { + return &ApplianceDatum{ + Credentials: creds, + BladeData: make(map[string]*BladeDatum), + ConnectionStatus: common.NOT_APPLICABLE, // Will use for single-BMC appliance } } -func (c *ApplianceDataStore) AddBlade(creds *openapi.Credentials) { - c.SavedBlades[creds.CustomId] = NewBladeDataStore(creds) +func (a *ApplianceDatum) AddBladeDatum(creds *openapi.Credentials) { + a.BladeData[creds.CustomId] = NewBladeDatum(creds) } -func (c *ApplianceDataStore) DeleteBlade(bladeId string) { - delete(c.SavedBlades, bladeId) +func (a *ApplianceDatum) DeleteBladeDatumById(bladeId string) { + delete(a.BladeData, bladeId) } -type BladeDataStore struct { - Credentials *openapi.Credentials `json:"credentials"` +func (a *ApplianceDatum) GetBladeDatumById(ctx context.Context, bladeId string) (*BladeDatum, error) { + logger := klog.FromContext(ctx) + + blade, exists := a.BladeData[bladeId] + if !exists { + err := fmt.Errorf("blade datum [%s] not found in appliance data [%s] in data store", bladeId, a.Credentials.CustomId) + logger.Error(err, "failure: update blade") + return nil, err + } + + return blade, nil +} + +func (a *ApplianceDatum) SetConnectionStatus(status common.ConnectionStatus) { + a.ConnectionStatus = status +} + +type BladeDatum struct { + Credentials *openapi.Credentials `json:"credentials"` + ConnectionStatus common.ConnectionStatus `json:"connection-status"` } -func NewBladeDataStore(creds *openapi.Credentials) *BladeDataStore { - return &BladeDataStore{ - Credentials: creds, +func NewBladeDatum(creds *openapi.Credentials) *BladeDatum { + return &BladeDatum{ + Credentials: creds, + ConnectionStatus: common.ONLINE, } } -type HostDataStore struct { - Credentials *openapi.Credentials `json:"credentials"` +func (b *BladeDatum) SetConnectionStatus(status *common.ConnectionStatus) { + b.ConnectionStatus = *status } -func NewHostDataStore(creds *openapi.Credentials) *HostDataStore { - return &HostDataStore{ - Credentials: creds, +type HostDatum struct { + Credentials *openapi.Credentials `json:"credentials"` + ConnectionStatus common.ConnectionStatus `json:"connection-status"` +} + +func NewHostDatum(creds *openapi.Credentials) *HostDatum { + return &HostDatum{ + Credentials: creds, + ConnectionStatus: common.ONLINE, } } +func (h *HostDatum) SetConnectionStatus(status *common.ConnectionStatus) { + h.ConnectionStatus = *status +} + //////////////////////////////////////// ///////////// Helpers ////////////////// //////////////////////////////////////// @@ -131,9 +159,8 @@ func ReloadDataStore(ctx context.Context, s openapi.DefaultAPIServicer, c *DataS logger.V(2).Info("cfm-service: restoring saved appliances") var appliancesToDelete []string - for applianceId, appliance := range c.SavedAppliances { - appliance.Credentials.CustomId = applianceId - _, err = s.AppliancesPost(ctx, *appliance.Credentials) + for applianceId, applianceDatum := range c.ApplianceData { + _, err = s.AppliancesPost(ctx, *applianceDatum.Credentials) if err != nil { logger.V(2).Info("cfm-service: appliance restore failure", "applianceId", applianceId) appliancesToDelete = append(appliancesToDelete, applianceId) @@ -141,9 +168,8 @@ func ReloadDataStore(ctx context.Context, s openapi.DefaultAPIServicer, c *DataS } bladesToDelete := make(map[string]string) - for bladeId, blade := range appliance.SavedBlades { - blade.Credentials.CustomId = bladeId - _, err = s.BladesPost(ctx, applianceId, *blade.Credentials) + for bladeId, bladeDatum := range applianceDatum.BladeData { + _, err = s.BladesPost(ctx, applianceId, *bladeDatum.Credentials) if err != nil { logger.V(2).Info("cfm-service: blade restore failure", "bladeId", bladeId, "applianceId", applianceId) bladesToDelete[applianceId] = bladeId @@ -151,27 +177,20 @@ func ReloadDataStore(ctx context.Context, s openapi.DefaultAPIServicer, c *DataS } for applianceId, bladeId := range bladesToDelete { - delete(c.SavedAppliances[applianceId].SavedBlades, bladeId) + delete(c.ApplianceData[applianceId].BladeData, bladeId) } } for _, applianceId := range appliancesToDelete { - delete(c.SavedAppliances, applianceId) + delete(c.ApplianceData, applianceId) } logger.V(2).Info("cfm-service: restoring saved hosts") - var hostsToDelete []string - for hostId, host := range c.SavedHosts { - host.Credentials.CustomId = hostId - _, err = s.HostsPost(ctx, *host.Credentials) + for hostId, hostDatum := range c.HostData { + _, err = s.HostsPost(ctx, *hostDatum.Credentials) if err != nil { - logger.V(2).Info("cfm-service: host restore failure", "hostId", hostId) - hostsToDelete = append(hostsToDelete, hostId) + logger.V(2).Info("cfm-service: host datum restore failure", "hostId", hostId) continue } } - - for _, hostId := range hostsToDelete { - delete(c.SavedHosts, hostId) - } } diff --git a/pkg/common/status.go b/pkg/common/status.go index a23b7dc..259308c 100644 --- a/pkg/common/status.go +++ b/pkg/common/status.go @@ -57,6 +57,8 @@ const ( StatusGetMemoryDevicesDetailsFailure //409 StatusApplianceResyncFailure //409 + StatusBladeResyncFailure //409 + StatusHostResyncFailure //409 StatusApplianceIdDuplicate //409 StatusBladeIdDuplicate //409 @@ -150,6 +152,10 @@ func (e StatusCodeType) String() string { return "Get Memory Devices Details Failure" case StatusApplianceResyncFailure: return "Appliance Resync Failure" + case StatusBladeResyncFailure: + return "Blade Resync Failure" + case StatusHostResyncFailure: + return "Host Resync Failure" case StatusBladeIdDoesNotExist: return "Blade Id Does Not Exist" case StatusAppliancesExceedMaximum: @@ -209,6 +215,8 @@ func (e StatusCodeType) HttpStatusCode() int { StatusGetMemoryDevicesFailure, StatusGetMemoryDevicesDetailsFailure, StatusApplianceResyncFailure, + StatusBladeResyncFailure, + StatusHostResyncFailure, StatusApplianceIdDuplicate, StatusBladeIdDuplicate, StatusPortIdDuplicate, diff --git a/pkg/manager/appliance.go b/pkg/manager/appliance.go index c1b256b..af5f991 100644 --- a/pkg/manager/appliance.go +++ b/pkg/manager/appliance.go @@ -8,6 +8,7 @@ import ( "cfm/pkg/backend" "cfm/pkg/common" + "cfm/pkg/common/datastore" "cfm/pkg/openapi" "github.com/google/uuid" @@ -24,15 +25,16 @@ type Appliance struct { } // NewAppliance - Creates a new Appliance object. -func NewAppliance(ctx context.Context, id string) (*Appliance, error) { +func NewAppliance(ctx context.Context, c *openapi.Credentials) (*Appliance, error) { logger := klog.FromContext(ctx) logger.V(4).Info(">>>>>> NewAppliance: ") - applianceId := id + applianceId := c.CustomId if applianceId == "" { // Generate uuid here and combine the last N digits with the prefix to be the appliance default id uuid := uuid.New().String() applianceId = fmt.Sprintf("%s-%s", ID_PREFIX_APPLIANCE_DFLT, uuid[(len(uuid)-common.NumUuidCharsForId):]) + c.CustomId = applianceId } // Check for duplicate ID @@ -104,6 +106,7 @@ func (a *Appliance) AddBlade(ctx context.Context, c *openapi.Credentials) (*Blad // Generate default id using last N digits of the session id combined with the default prefix bladeId = fmt.Sprintf("%s-%s", ID_PREFIX_BLADE_DFLT, response.SessionId[(len(response.SessionId)-common.NumUuidCharsForId):]) } + c.CustomId = bladeId } // Check for duplicate ID @@ -127,6 +130,7 @@ func (a *Appliance) AddBlade(ctx context.Context, c *openapi.Credentials) (*Blad BladeId: bladeId, ApplianceId: a.Id, Ip: c.IpAddress, + Status: common.ONLINE, Port: uint16(c.Port), BackendOps: ops, Creds: c, @@ -150,28 +154,28 @@ func (a *Appliance) AddBlade(ctx context.Context, c *openapi.Credentials) (*Blad // Add blade to appliance a.Blades[blade.Id] = blade + // Add host to datastore + applianceDatum, _ := datastore.DStore().GetDataStore().GetApplianceDatumById(a.Id) + applianceDatum.AddBladeDatum(c) + datastore.DStore().Store() + logger.V(2).Info("success: add blade", "bladeId", blade.Id, "applianceId", a.Id) return blade, nil } -func (a *Appliance) DeleteAllBlades(ctx context.Context) error { +func (a *Appliance) DeleteAllBlades(ctx context.Context) { logger := klog.FromContext(ctx) logger.V(4).Info(">>>>>> DeleteAllBlades: ", "applianceId", a.Id) for id := range a.Blades { - _, err := a.DeleteBladeById(ctx, id) - if err != nil { - return err - } + a.DeleteBladeById(ctx, id) // ignore any errors } logger.V(2).Info("success: delete all blades", "applianceId", a.Id) - - return nil } -// DeleteBladeById: Delete the blade backend session and the local blade cache +// DeleteBladeById: Delete the blade from: backend, deviceCache and datastore func (a *Appliance) DeleteBladeById(ctx context.Context, bladeId string) (*Blade, error) { logger := klog.FromContext(ctx) logger.V(4).Info(">>>>>> DeleteBladeById: ", "bladeId", bladeId, "applianceId", a.Id) @@ -181,6 +185,10 @@ func (a *Appliance) DeleteBladeById(ctx context.Context, bladeId string) (*Blade if !ok { logger.V(2).Info("blade not found during delete:", "bladeId", bladeId, "applianceId", a.Id) newErr := fmt.Errorf("blade [%s] not found during delete", bladeId) + + logger.V(2).Info("force complete appliance blade deletion after error", "bladeId", blade.Id, "applianceId", a.Id) + a.deleteBlade(bladeId) + return nil, &common.RequestError{StatusCode: common.StatusBladeIdDoesNotExist, Err: newErr} } @@ -196,15 +204,14 @@ func (a *Appliance) DeleteBladeById(ctx context.Context, bladeId string) (*Blade newErr := fmt.Errorf("failed to delete blade [%s] backend [%s] session [%s]: %w", blade.Id, ops.GetBackendInfo(ctx).BackendName, blade.Socket.String(), err) logger.Error(newErr, "failure: delete blade by id") - // Currently, backend ALWAYS deletes the blade session from the backend map. For now, need to delete blade from appliance map as well. - logger.V(2).Info("force blade deletion after backend session failure", "bladeId", blade.Id, "applianceId", a.Id) - delete(a.Blades, blade.Id) + // Currently, backend ALWAYS deletes the blade session from the backend map. Do the same in the this (manager) layer + logger.V(2).Info("force complete appliance blade deletion after backend session failure", "bladeId", blade.Id, "applianceId", a.Id) + a.deleteBlade(bladeId) return nil, &common.RequestError{StatusCode: common.StatusBladeDeleteSessionFailure, Err: newErr} } - // delete blade - delete(a.Blades, blade.Id) + a.deleteBlade(bladeId) logger.V(2).Info("success: delete blade by id", "bladeId", blade.Id, "applianceId", a.Id) @@ -232,7 +239,9 @@ func (a *Appliance) GetBladeById(ctx context.Context, bladeId string) (*Blade, e return nil, &common.RequestError{StatusCode: common.StatusBladeIdDoesNotExist, Err: newErr} } - logger.V(2).Info("success: get blade by id", "bladeId", blade.Id, "applianceId", a.Id) + blade.UpdateConnectionStatusBackend(ctx) + + logger.V(2).Info("success: get blade by id", "status", blade.Status, "bladeId", blade.Id, "applianceId", a.Id) return blade, nil } @@ -291,20 +300,31 @@ func (a *Appliance) ResyncBladeById(ctx context.Context, bladeId string) (*Blade logger := klog.FromContext(ctx) logger.V(4).Info(">>>>>> ResyncBladeById: ", "bladeId", bladeId, "applianceId", a.Id) - blade, err := a.DeleteBladeById(ctx, bladeId) - if err != nil { - newErr := fmt.Errorf("failed to resync blade(delete): appliance [%s] blade [%s]: %w", a.Id, bladeId, err) - logger.Error(newErr, "failure: resync blade: ignoring") + // query device cache + blade, ok := deviceCache.GetBladeByIdOk(a.Id, bladeId) + if !ok || blade == nil { + newErr := fmt.Errorf("failed to get blade [%s]", bladeId) + logger.Error(newErr, "failure: resync blade by id") + return nil, &common.RequestError{StatusCode: common.StatusHostIdDoesNotExist, Err: newErr} } - blade, err = a.AddBlade(ctx, blade.creds) - if err != nil { - newErr := fmt.Errorf("failed to resync blade(add): appliance [%s] blade [%s]: %w", a.Id, bladeId, err) - logger.Error(newErr, "failure: resync blade") - return nil, &common.RequestError{StatusCode: err.(*common.RequestError).StatusCode, Err: newErr} - } + blade.UpdateConnectionStatusBackend(ctx) - logger.V(2).Info("success: resync blade", "bladeId", bladeId, "applianceId", a.Id) + logger.V(2).Info("success: resync blade", "status", blade.Status, "bladeId", bladeId, "applianceId", a.Id) return blade, nil } + +///////////////////////////////////// +//////// Private Functions ////////// +///////////////////////////////////// + +func (a *Appliance) deleteBlade(bladeId string) { + // delete blade from manager cache + delete(a.Blades, bladeId) + + // delete blade from datastore + applianceDatum, _ := datastore.DStore().GetDataStore().GetApplianceDatumById(a.Id) + applianceDatum.DeleteBladeDatumById(bladeId) + datastore.DStore().Store() +} diff --git a/pkg/manager/blade.go b/pkg/manager/blade.go index 32753ad..8b752a7 100644 --- a/pkg/manager/blade.go +++ b/pkg/manager/blade.go @@ -11,6 +11,7 @@ import ( "cfm/pkg/backend" "cfm/pkg/common" + "cfm/pkg/common/datastore" "cfm/pkg/openapi" "k8s.io/klog/v2" @@ -19,9 +20,9 @@ import ( const ID_PREFIX_BLADE_DFLT string = "blade" type Blade struct { - Id string - Uri string - // Status string // Meaningless without async update + Id string + Uri string + Status common.ConnectionStatus Socket SocketDetails ApplianceId string Memory map[string]*BladeMemory @@ -42,6 +43,7 @@ type RequestNewBlade struct { Ip string Port uint16 ApplianceId string + Status common.ConnectionStatus BackendOps backend.BackendOperations Creds *openapi.Credentials } @@ -55,6 +57,7 @@ func NewBlade(ctx context.Context, r *RequestNewBlade) (*Blade, error) { Uri: GetCfmUriBladeId(r.ApplianceId, r.BladeId), Socket: *NewSocketDetails(r.Ip, r.Port), ApplianceId: r.ApplianceId, + Status: r.Status, Ports: make(map[string]*CxlBladePort), Resources: make(map[string]*BladeResource), Memory: make(map[string]*BladeMemory), @@ -125,7 +128,7 @@ func (b *Blade) AssignMemory(ctx context.Context, r *RequestAssignMemory) (*open if len(memory.resourceIds) != 0 { resourcesToUpdate = memory.resourceIds } else { - resourcesToUpdate = b.GetAllResourceIds() + resourcesToUpdate = b.GetAllResourceIds(ctx) } for _, resourceId := range resourcesToUpdate { resource, err := b.GetResourceById(ctx, resourceId) @@ -330,7 +333,7 @@ func (b *Blade) FreeMemoryById(ctx context.Context, memoryId string) (*openapi.M if len(memory.resourceIds) != 0 { resourcesToUpdate = memory.resourceIds } else { - resourcesToUpdate = b.GetAllResourceIds() + resourcesToUpdate = b.GetAllResourceIds(ctx) } for _, resourceId := range resourcesToUpdate { resource, err := b.GetResourceById(ctx, resourceId) @@ -362,33 +365,30 @@ func (b *Blade) FreeMemoryById(ctx context.Context, memoryId string) (*openapi.M return &memoryRegion, nil } -func (b *Blade) GetAllMemoryIds() []string { +func (b *Blade) GetAllMemoryIds(ctx context.Context) []string { var ids []string - // CACHE: Get - for id := range b.Memory { + for id := range b.GetMemory(ctx) { ids = append(ids, id) } return ids } -func (b *Blade) GetAllPortIds() []string { +func (b *Blade) GetAllPortIds(ctx context.Context) []string { var ids []string - // CACHE: Get - for id := range b.Ports { + for id := range b.GetPorts(ctx) { ids = append(ids, id) } return ids } -func (b *Blade) GetAllResourceIds() []string { +func (b *Blade) GetAllResourceIds(ctx context.Context) []string { var ids []string - // CACHE: Get - for id := range b.Resources { + for id := range b.GetResources(ctx) { ids = append(ids, id) } @@ -399,6 +399,11 @@ func (b *Blade) GetMemoryById(ctx context.Context, memoryId string) (*BladeMemor logger := klog.FromContext(ctx) logger.V(4).Info(">>>>>> GetMemoryById: ", "memoryId", memoryId, "bladeId", b.Id, "applianceId", b.ApplianceId) + if !b.IsOnline(ctx) { + // If blade offline, not an error. Just no information to return. + return nil, nil + } + memory, ok := b.Memory[memoryId] if !ok { newErr := fmt.Errorf("memory [%s] not found on appliance [%s] blade [%s] ", memoryId, b.ApplianceId, b.Id) @@ -415,6 +420,11 @@ func (b *Blade) GetMemory(ctx context.Context) map[string]*BladeMemory { logger := klog.FromContext(ctx) logger.V(4).Info(">>>>>> GetMemory: ", "bladeId", b.Id, "applianceId", b.ApplianceId) + if !b.IsOnline(ctx) { + // If blade offline, not an error. Just no information to return. + return make(map[string]*BladeMemory) + } + memory := b.Memory logger.V(2).Info("success: get memory", "count", len(memory), "bladeId", b.Id, "applianceId", b.ApplianceId) @@ -427,9 +437,12 @@ func (b *Blade) GetMemoryBackend(ctx context.Context) ([]string, error) { logger := klog.FromContext(ctx) logger.V(4).Info(">>>>>> GetMemoryBackend: ", "bladeId", b.Id, "applianceId", b.ApplianceId) - // HARDWARE: Get + if !b.IsOnline(ctx) { + // If blade offline, not an error. Just no information to return. + return make([]string, 0), nil + } + req := backend.GetMemoryRequest{} - // get memory ids from backend response, err := b.backendOps.GetMemory(ctx, &backend.ConfigurationSettings{}, &req) if err != nil || response == nil { newErr := fmt.Errorf("get memory (backend) [%s] failure on blade [%s]: %w", b.backendOps.GetBackendInfo(ctx).BackendName, b.Id, err) @@ -454,6 +467,11 @@ func (b *Blade) GetPortById(ctx context.Context, portId string) (*CxlBladePort, logger := klog.FromContext(ctx) logger.V(4).Info(">>>>>> GetPortById: ", "portId", portId, "bladeId", b.Id, "applianceId", b.ApplianceId) + if !b.IsOnline(ctx) { + // If blade offline, not an error. Just no information to return. + return nil, nil + } + port, ok := b.Ports[portId] if !ok { newErr := fmt.Errorf("port [%s] not found on appliance [%s] blade [%s] ", portId, b.ApplianceId, b.Id) @@ -470,6 +488,11 @@ func (b *Blade) GetPorts(ctx context.Context) map[string]*CxlBladePort { logger := klog.FromContext(ctx) logger.V(4).Info(">>>>>> GetPorts: ", "bladeId", b.Id, "applianceId", b.ApplianceId) + if !b.IsOnline(ctx) { + // If blade offline, not an error. Just no information to return. + return make(map[string]*CxlBladePort) + } + ports := b.Ports logger.V(2).Info("success: get ports(blade) (cache)", "count", len(ports), "bladeId", b.Id, "applianceId", b.ApplianceId) @@ -482,6 +505,11 @@ func (b *Blade) GetPortsBackend(ctx context.Context) ([]string, error) { logger := klog.FromContext(ctx) logger.V(4).Info(">>>>>> GetPortsBackend: ", "bladeId", b.Id, "applianceId", b.ApplianceId) + if !b.IsOnline(ctx) { + // If blade offline, not an error. Just no information to return. + return make([]string, 0), nil + } + req := backend.GetPortsRequest{} response, err := b.backendOps.GetPorts(ctx, &backend.ConfigurationSettings{}, &req) if err != nil || response == nil { @@ -499,6 +527,11 @@ func (b *Blade) GetResourceById(ctx context.Context, resourceId string) (*BladeR logger := klog.FromContext(ctx) logger.V(4).Info(">>>>>> GetResourceById: ", "resourceId", resourceId, "bladeId", b.Id, "applianceId", b.ApplianceId) + if !b.IsOnline(ctx) { + // If blade offline, not an error. Just no information to return. + return nil, nil + } + resource, ok := b.Resources[resourceId] if !ok { newErr := fmt.Errorf("resource [%s] not found on appliance [%s] blade [%s] ", resourceId, b.ApplianceId, b.Id) @@ -515,6 +548,11 @@ func (b *Blade) GetResources(ctx context.Context) map[string]*BladeResource { logger := klog.FromContext(ctx) logger.V(4).Info(">>>>>> GetResources: ", "bladeId", b.Id, "applianceId", b.ApplianceId) + if !b.IsOnline(ctx) { + // If blade offline, not an error. Just no information to return. + return make(map[string]*BladeResource) + } + resources := b.Resources logger.V(2).Info("success: get resources(cache)", "count", len(resources), "bladeId", b.Id, "applianceId", b.ApplianceId) @@ -527,6 +565,11 @@ func (b *Blade) GetResourcesBackend(ctx context.Context) ([]string, error) { logger := klog.FromContext(ctx) logger.V(4).Info(">>>>>> GetResourcesBackend: ", "bladeId", b.Id, "applianceId", b.ApplianceId) + if !b.IsOnline(ctx) { + // If blade offline, not an error. Just no information to return. + return make([]string, 0), nil + } + req := backend.MemoryResourceBlocksRequest{} response, err := b.backendOps.GetMemoryResourceBlocks(ctx, &backend.ConfigurationSettings{}, &req) if err != nil || response == nil { @@ -546,8 +589,12 @@ func (b *Blade) GetResourceTotals(ctx context.Context) (*ResponseResourceTotals, var totalAvail, totalAlloc int32 - resources := b.GetResources(ctx) - for _, resource := range resources { + response := ResponseResourceTotals{ + TotalMemoryAvailableMiB: 0, + TotalMemoryAllocatedMiB: 0, + } + + for _, resource := range b.GetResources(ctx) { totals, err := resource.GetTotals(ctx) if err != nil || totals == nil { newErr := fmt.Errorf("failed to get resource totals: appliance [%s] blade [%s] resource [%s]: %w", b.ApplianceId, b.Id, resource.Id, err) @@ -559,12 +606,10 @@ func (b *Blade) GetResourceTotals(ctx context.Context) (*ResponseResourceTotals, totalAlloc += totals.TotalMemoryAllocatedMiB } - response := ResponseResourceTotals{ - TotalMemoryAvailableMiB: totalAvail, - TotalMemoryAllocatedMiB: totalAlloc, - } + response.TotalMemoryAvailableMiB = totalAvail + response.TotalMemoryAllocatedMiB = totalAlloc - logger.V(2).Info("success: get resource totals", "bladeId", b.Id, "applianceId", b.ApplianceId) + logger.V(2).Info("success: get resource totals", "totals", response, "bladeId", b.Id, "applianceId", b.ApplianceId) return &response, nil } @@ -583,6 +628,32 @@ func (b *Blade) InvalidateCache() { } } +func (b *Blade) IsOnline(ctx context.Context) bool { + return b.Status == common.ONLINE +} + +// UpdateConnectionStatusBackend - Query the blade root service to verify continued connection and update the object status accordingly. +func (b *Blade) UpdateConnectionStatusBackend(ctx context.Context) { + logger := klog.FromContext(ctx) + logger.V(4).Info(">>>>>> UpdateConnectionStatusBackend: ", "bladeId", b.Id) + + req := backend.GetRootServiceRequest{} + response, err := b.backendOps.GetRootService(ctx, &backend.ConfigurationSettings{}, &req) + if err != nil || response == nil { + b.Status = common.OFFLINE + } else { + b.Status = common.ONLINE + } + + // Update datastore status + applianceDatum, _ := datastore.DStore().GetDataStore().GetApplianceDatumById(b.ApplianceId) + bladeDatum, _ := applianceDatum.GetBladeDatumById(ctx, b.Id) + bladeDatum.SetConnectionStatus(&b.Status) + datastore.DStore().Store() + + logger.V(2).Info("update blade status(backend)", "status", b.Status, "bladeId", b.Id) +} + ///////////////////////////////////// //////// Private Functions ////////// ///////////////////////////////////// diff --git a/pkg/manager/host.go b/pkg/manager/host.go index 2913577..a2d650d 100644 --- a/pkg/manager/host.go +++ b/pkg/manager/host.go @@ -9,6 +9,7 @@ import ( "cfm/pkg/backend" "cfm/pkg/common" + "cfm/pkg/common/datastore" "cfm/pkg/openapi" "k8s.io/klog/v2" @@ -17,9 +18,9 @@ import ( const ID_PREFIX_HOST_DFLT string = "host" type Host struct { - Id string - Uri string - // Status string // Meaningless without async update + Id string + Uri string + Status common.ConnectionStatus Socket SocketDetails Ports map[string]*CxlHostPort MemoryDevices map[string]*HostMemoryDevice @@ -39,6 +40,7 @@ type RequestNewHost struct { HostId string Ip string Port uint16 + Status common.ConnectionStatus BackendOps backend.BackendOperations Creds *openapi.Credentials } @@ -51,6 +53,7 @@ func NewHost(ctx context.Context, r *RequestNewHost) (*Host, error) { Id: r.HostId, Uri: GetCfmUriHostId(r.HostId), Socket: *NewSocketDetails(r.Ip, r.Port), + Status: r.Status, Ports: make(map[string]*CxlHostPort), MemoryDevices: make(map[string]*HostMemoryDevice), Memory: make(map[string]*HostMemory), @@ -160,30 +163,30 @@ func (h *Host) FreeMemoryById(ctx context.Context, hostMemoryId string) (*openap return memory, nil } -func (h *Host) GetAllMemoryIds() []string { +func (h *Host) GetAllMemoryIds(ctx context.Context) []string { var ids []string - for id := range h.Memory { + for id := range h.GetMemory(ctx) { ids = append(ids, id) } return ids } -func (h *Host) GetAllMemoryDeviceIds() []string { +func (h *Host) GetAllMemoryDeviceIds(ctx context.Context) []string { var ids []string - for id := range h.MemoryDevices { + for id := range h.GetMemoryDevices(ctx) { ids = append(ids, id) } return ids } -func (h *Host) GetAllPortIds() []string { +func (h *Host) GetAllPortIds(ctx context.Context) []string { var ids []string - for id := range h.Ports { + for id := range h.GetPorts(ctx) { ids = append(ids, id) } @@ -194,6 +197,11 @@ func (h *Host) GetMemoryById(ctx context.Context, memoryId string) (*HostMemory, logger := klog.FromContext(ctx) logger.V(4).Info(">>>>>> GetMemoryById: ", "memoryId", memoryId, "hostId", h.Id) + if !h.IsOnline(ctx) { + // If host offline, not an error. Just no information to return. + return nil, nil + } + memory, ok := h.Memory[memoryId] if !ok { newErr := fmt.Errorf("memory [%s] not found on host [%s]", memoryId, h.Id) @@ -210,6 +218,11 @@ func (h *Host) GetMemory(ctx context.Context) map[string]*HostMemory { logger := klog.FromContext(ctx) logger.V(4).Info(">>>>>> GetMemory: ", "hostId", h.Id) + if !h.IsOnline(ctx) { + // If host offline, not an error. Just no information to return. + return make(map[string]*HostMemory) + } + memory := h.Memory logger.V(2).Info("success: get memory", "count", len(memory), "hostId", h.Id) @@ -222,6 +235,11 @@ func (h *Host) GetMemoryBackend(ctx context.Context) ([]string, error) { logger := klog.FromContext(ctx) logger.V(4).Info(">>>>>> GetMemoryBackend: ", "hostId", h.Id) + if !h.IsOnline(ctx) { + // If host offline, not an error. Just no information to return. + return make([]string, 0), nil + } + req := backend.GetMemoryRequest{} response, err := h.backendOps.GetMemory(ctx, &backend.ConfigurationSettings{}, &req) if err != nil || response == nil { @@ -237,6 +255,11 @@ func (h *Host) GetMemoryDeviceById(ctx context.Context, memdevId string) (*HostM logger := klog.FromContext(ctx) logger.V(4).Info(">>>>>> GetMemoryDeviceById: ", "memdevId", memdevId, "hostId", h.Id) + if !h.IsOnline(ctx) { + // If host offline, not an error. Just no information to return. + return nil, nil + } + memdev, ok := h.MemoryDevices[memdevId] if !ok { newErr := fmt.Errorf("memory device [%s] not found on host [%s]", memdevId, h.Id) @@ -253,6 +276,11 @@ func (h *Host) GetMemoryDevices(ctx context.Context) map[string]*HostMemoryDevic logger := klog.FromContext(ctx) logger.V(4).Info(">>>>>> GetMemoryDevices: ", "hostId", h.Id) + if !h.IsOnline(ctx) { + // If host offline, not an error. Just no information to return. + return make(map[string]*HostMemoryDevice) + } + memdevs := h.MemoryDevices logger.V(2).Info("success: get memory devices", "count", len(memdevs), "hostId", h.Id) @@ -283,7 +311,7 @@ func (h *Host) GetMemoryDomainAllMemoryIds(ctx context.Context, domain string) ( typeString, exist := HostMemoryDomain[domain] if exist { - for id, mem := range h.Memory { + for id, mem := range h.GetMemory(ctx) { details, err := mem.GetDetails(ctx) if err != nil { return nil, err @@ -308,6 +336,11 @@ func (h *Host) GetMemoryDevicesBackend(ctx context.Context) (map[string][]string logger := klog.FromContext(ctx) logger.V(4).Info(">>>>>> GetMemoryDevicesBackend: ", "hostId", h.Id) + if !h.IsOnline(ctx) { + // If host offline, not an error. Just no information to return. + return make(map[string][]string, 0), nil + } + req := backend.GetMemoryDevicesRequest{} response, err := h.backendOps.GetMemoryDevices(ctx, &backend.ConfigurationSettings{}, &req) if err != nil || response == nil { @@ -321,13 +354,23 @@ func (h *Host) GetMemoryDevicesBackend(ctx context.Context) (map[string][]string return response.DeviceIdMap, nil } +type ResponseHostMemoryTotals struct { + LocalMemoryMib int32 + RemoteMemoryMib int32 +} + func (h *Host) GetMemoryTotals(ctx context.Context) (*ResponseHostMemoryTotals, error) { logger := klog.FromContext(ctx) logger.V(4).Info(">>>>>> GetMemoryTotals: ", "hostId", h.Id) var local, remote int32 - for _, memory := range h.Memory { + response := ResponseHostMemoryTotals{ + LocalMemoryMib: 0, + RemoteMemoryMib: 0, + } + + for _, memory := range h.GetMemory(ctx) { totals, err := memory.GetTotals(ctx) if err != nil || totals == nil { newErr := fmt.Errorf("failed to get memory totals: host [%s] memory [%s]: %w", h.Id, memory.Id, err) @@ -339,12 +382,10 @@ func (h *Host) GetMemoryTotals(ctx context.Context) (*ResponseHostMemoryTotals, remote += totals.RemoteMemoryMib } - response := ResponseHostMemoryTotals{ - LocalMemoryMib: local, - RemoteMemoryMib: remote, - } + response.LocalMemoryMib = local + response.RemoteMemoryMib = remote - logger.V(2).Info("success: get memory totals", "hostId", h.Id) + logger.V(2).Info("success: get memory totals", "totals", response, "hostId", h.Id) return &response, nil } @@ -353,6 +394,11 @@ func (h *Host) GetPortById(ctx context.Context, portId string) (*CxlHostPort, er logger := klog.FromContext(ctx) logger.V(4).Info(">>>>>> GetPortById: ", "portId", portId, "hostId", h.Id) + if !h.IsOnline(ctx) { + // If host offline, not an error. Just no information to return. + return nil, nil + } + port, ok := h.Ports[portId] if !ok { newErr := fmt.Errorf("port [%s] not found on host [%s]", portId, h.Id) @@ -369,6 +415,11 @@ func (h *Host) GetPorts(ctx context.Context) map[string]*CxlHostPort { logger := klog.FromContext(ctx) logger.V(4).Info(">>>>>> GetPorts: ", "hostId", h.Id) + if !h.IsOnline(ctx) { + // If host offline, not an error. Just no information to return. + return make(map[string]*CxlHostPort) + } + ports := h.Ports logger.V(2).Info("success: get ports", "count", len(ports), "hostId", h.Id) @@ -381,6 +432,11 @@ func (h *Host) GetPortsBackend(ctx context.Context) ([]string, error) { logger := klog.FromContext(ctx) logger.V(4).Info(">>>>>> GetPortsBackend: ", "hostId", h.Id) + if !h.IsOnline(ctx) { + // If host offline, not an error. Just no information to return. + return make([]string, 0), nil + } + req := backend.GetPortsRequest{} response, err := h.backendOps.GetHostPortPcieDevices(ctx, &backend.ConfigurationSettings{}, &req) if err != nil || response == nil { @@ -416,9 +472,29 @@ func (h *Host) InvalidateCache() { } } -type ResponseHostMemoryTotals struct { - LocalMemoryMib int32 - RemoteMemoryMib int32 +func (h *Host) IsOnline(ctx context.Context) bool { + return h.Status == common.ONLINE +} + +// UpdateConnectionStatusBackend - Query the host root service to verify continued connection and update the object status accordingly. +func (h *Host) UpdateConnectionStatusBackend(ctx context.Context) { + logger := klog.FromContext(ctx) + logger.V(4).Info(">>>>>> UpdateConnectionStatusBackend: ", "hostId", h.Id) + + req := backend.GetRootServiceRequest{} + response, err := h.backendOps.GetRootService(ctx, &backend.ConfigurationSettings{}, &req) + if err != nil || response == nil { + h.Status = common.OFFLINE + } else { + h.Status = common.ONLINE + } + + // Update datastore status + hostDatum, _ := datastore.DStore().GetDataStore().GetHostDatumById(h.Id) + hostDatum.SetConnectionStatus(&h.Status) + datastore.DStore().Store() + + logger.V(2).Info("update host status(backend)", "status", h.Status, "hostId", h.Id) } ///////////////////////////////////// diff --git a/pkg/manager/manager.go b/pkg/manager/manager.go index e010b70..fc20617 100644 --- a/pkg/manager/manager.go +++ b/pkg/manager/manager.go @@ -8,6 +8,7 @@ import ( "cfm/pkg/backend" "cfm/pkg/common" + "cfm/pkg/common/datastore" "cfm/pkg/openapi" "k8s.io/klog/v2" @@ -42,20 +43,19 @@ func AddAppliance(ctx context.Context, c *openapi.Credentials) (*Appliance, erro logger.V(4).Info(">>>>>> AddAppliance: ") // Create a new cfm-service Appliance object - appliance, err := NewAppliance(ctx, c.CustomId) + appliance, err := NewAppliance(ctx, c) if err != nil || appliance == nil { newErr := fmt.Errorf("new appliance creation failure: %w", err) logger.Error(newErr, "failure: add appliance") return nil, &common.RequestError{StatusCode: err.(*common.RequestError).StatusCode, Err: newErr} } - // Cache it - err = deviceCache.AddAppliance(appliance) - if err != nil { - newErr := fmt.Errorf("add appliance [%s] failure: %w", appliance.Id, err) - logger.Error(newErr, "failure: add appliance") - return nil, &common.RequestError{StatusCode: err.(*common.RequestError).StatusCode, Err: newErr} - } + // Add appliance to device cache + deviceCache.AddAppliance(appliance) // ignore error, duplicate check done above + + // Add appliance to datastore + datastore.DStore().GetDataStore().AddApplianceDatum(c) + datastore.DStore().Store() logger.V(2).Info("success: add appliance", "applianceId", appliance.Id) @@ -74,24 +74,18 @@ func DeleteApplianceById(ctx context.Context, applianceId string) (*Appliance, e return nil, &common.RequestError{StatusCode: common.StatusApplianceIdDoesNotExist, Err: newErr} } - err := appliance.DeleteAllBlades(ctx) // cache and hardware interactions here - if err != nil { - newErr := fmt.Errorf("failed to delete all appliance [%s] blades: %w", appliance.Id, err) - logger.Error(newErr, "failure: delete appliance by id") - return nil, &common.RequestError{StatusCode: err.(*common.RequestError).StatusCode, Err: newErr} - } + appliance.DeleteAllBlades(ctx) // delete appliance from cache - a := deviceCache.DeleteApplianceById(appliance.Id) - if a == nil { - newErr := fmt.Errorf("appliance [%s] cache delete failed", appliance.Id) - logger.Error(newErr, "failure: delete appliance by id") - return nil, &common.RequestError{StatusCode: common.StatusApplianceDeleteSessionFailure, Err: newErr} - } + deviceCache.DeleteApplianceById(appliance.Id) + + // delete appliance from datastore + datastore.DStore().GetDataStore().DeleteApplianceDatumById(appliance.Id) + datastore.DStore().Store() - logger.V(2).Info("success: delete appliance by id", "applianceId", a.Id) + logger.V(2).Info("success: delete appliance by id", "applianceId", appliance.Id) - return a, nil + return appliance, nil } func GetAllApplianceIds() []string { @@ -125,7 +119,7 @@ func GetAppliances(ctx context.Context) map[string]*Appliance { return appliances } -func ResyncApplianceById(ctx context.Context, applianceId string) (*Appliance, *[]string, error) { +func ResyncApplianceById(ctx context.Context, applianceId string) (*Appliance, error) { logger := klog.FromContext(ctx) logger.V(4).Info(">>>>>> ResyncApplianceById: ", "applianceId", applianceId) @@ -135,7 +129,7 @@ func ResyncApplianceById(ctx context.Context, applianceId string) (*Appliance, * if err != nil { newErr := fmt.Errorf("get appliance by id [%s] failure: %w", appliance.Id, err) logger.Error(newErr, "failure: resync appliance by id") - return nil, &failedBladeIds, &common.RequestError{StatusCode: err.(*common.RequestError).StatusCode, Err: newErr} + return nil, &common.RequestError{StatusCode: err.(*common.RequestError).StatusCode, Err: newErr} } bladeIds := appliance.GetAllBladeIds() @@ -151,15 +145,15 @@ func ResyncApplianceById(ctx context.Context, applianceId string) (*Appliance, * if len(failedBladeIds) == 0 { logger.V(2).Info("success: resync appliance", "applianceId", applianceId, "bladeIds", bladeIds) - return appliance, &failedBladeIds, nil + return appliance, nil } else if len(failedBladeIds) < len(bladeIds) { newErr := fmt.Errorf("resync appliance by id [%s]: some failure(s): blade(s) [%s]: %w", appliance.Id, failedBladeIds, err) logger.Error(newErr, "partial success: resync appliance by id") - return appliance, &failedBladeIds, &common.RequestError{StatusCode: common.StatusApplianceResyncPartialSuccess, Err: newErr} + return appliance, &common.RequestError{StatusCode: common.StatusApplianceResyncPartialSuccess, Err: newErr} } else { newErr := fmt.Errorf("resync appliance by id [%s] failure: %w", appliance.Id, err) logger.Error(newErr, "failure: resync appliance by id") - return nil, &failedBladeIds, &common.RequestError{StatusCode: common.StatusApplianceResyncFailure, Err: newErr} + return nil, &common.RequestError{StatusCode: common.StatusApplianceResyncFailure, Err: newErr} } } @@ -217,6 +211,7 @@ func AddHost(ctx context.Context, c *openapi.Credentials) (*Host, error) { // Generate default id using last N digits of the session id combined with the default prefix hostId = fmt.Sprintf("%s-%s", ID_PREFIX_HOST_DFLT, response.SessionId[(len(response.SessionId)-common.NumUuidCharsForId):]) } + c.CustomId = hostId } // Check for duplicate ID. @@ -240,6 +235,7 @@ func AddHost(ctx context.Context, c *openapi.Credentials) (*Host, error) { HostId: hostId, Ip: c.IpAddress, Port: uint16(c.Port), + Status: common.ONLINE, BackendOps: ops, Creds: c, } @@ -259,8 +255,12 @@ func AddHost(ctx context.Context, c *openapi.Credentials) (*Host, error) { return nil, &common.RequestError{StatusCode: common.StatusManagerInitializationFailure, Err: newErr} } - // Cache it - deviceCache.AddHost(host) + // Add host to device cache + deviceCache.AddHost(host) // ignore error, duplicate check done above + + // Add host to datastore + datastore.DStore().GetDataStore().AddHostDatum(c) + datastore.DStore().Store() logger.V(2).Info("success: add host", "hostId", host.Id) @@ -290,24 +290,20 @@ func DeleteHostById(ctx context.Context, hostId string) (*Host, error) { newErr := fmt.Errorf("failed to delete host [%s] backend [%s] session [%s]: %w", host.Id, ops.GetBackendInfo(ctx).BackendName, host.Socket.String(), err) logger.Error(newErr, "failure: delete host by id") - // Currently, backend ALWAYS deletes the host session from the backend map. For now, need to delete host from manager map as well. - logger.V(2).Info("force host deletion after backend session failure", "hostId", host.Id) - deviceCache.DeleteHostById(host.Id) + // Currently, backend ALWAYS deletes the host session from the backend map. + // Delete host from manager cache and datastore as well + logger.V(2).Info("force host deletion after backend delete session failure", "hostId", host.Id) - return nil, &common.RequestError{StatusCode: common.StatusHostDeleteSessionFailure, Err: newErr} - } + deleteHost(host.Id) - // delete host from cache - h := deviceCache.DeleteHostById(host.Id) - if h == nil { - newErr := fmt.Errorf("host [%s] cache delete failed", host.Id) - logger.Error(newErr, "failure: delete host by id") - return nil, &common.RequestError{StatusCode: common.StatusHostDeleteSessionFailure, Err: newErr} + return host, &common.RequestError{StatusCode: common.StatusHostDeleteSessionFailure, Err: newErr} } - logger.V(2).Info("success: delete host by id", "hostId", h.Id) + deleteHost(host.Id) + + logger.V(2).Info("success: delete host by id", "hostId", host.Id) - return h, nil + return host, nil } func GetAllHostIds() []string { @@ -322,11 +318,13 @@ func GetHostById(ctx context.Context, hostId string) (*Host, error) { host, err := deviceCache.GetHostById(hostId) if err != nil { logger.Error(err, "failure: get host by id") - newErr := fmt.Errorf("failure: get host by id [%s]", hostId) + newErr := fmt.Errorf("failure: get host by id [%s]: %w", hostId, err) return nil, &common.RequestError{StatusCode: err.(*common.RequestError).StatusCode, Err: newErr} } - logger.V(2).Info("success: get host by id", "hostId", hostId) + host.UpdateConnectionStatusBackend(ctx) + + logger.V(2).Info("success: get host by id", "status", host.Status, "hostId", host.Id) return host, nil } @@ -346,20 +344,32 @@ func ResyncHostById(ctx context.Context, hostId string) (*Host, error) { logger := klog.FromContext(ctx) logger.V(4).Info(">>>>>> ResyncHostById: ", "hostId", hostId) - host, err := DeleteHostById(ctx, hostId) - if err != nil { - newErr := fmt.Errorf("failed to resync host(delete): host [%s]: %w", hostId, err) - logger.Error(newErr, "failure: resync host: ignoring") + // query device cache + host, ok := deviceCache.GetHostByIdOk(hostId) + if !ok || host == nil { + newErr := fmt.Errorf("failed to get host [%s]", hostId) + logger.Error(newErr, "failure: resync host by id") + return nil, &common.RequestError{StatusCode: common.StatusHostIdDoesNotExist, Err: newErr} } - host, err = AddHost(ctx, host.creds) - if err != nil { - newErr := fmt.Errorf("failed to resync host(add): host [%s]: %w", hostId, err) - logger.Error(newErr, "failure: resync host") - return nil, &common.RequestError{StatusCode: err.(*common.RequestError).StatusCode, Err: newErr} - } + host.UpdateConnectionStatusBackend(ctx) - logger.V(2).Info("success: resync host", "hostId", hostId) + logger.V(2).Info("success: resync host", "status", host.Status, "hostId", host.Id) return host, nil } + +//////////////////////////////////// +//////// Helper Functions ////////// +//////////////////////////////////// + +func deleteHost(hostId string) *Host { + // delete host from manager cache + h := deviceCache.DeleteHostById(hostId) + + // delete host from datastore + datastore.DStore().GetDataStore().DeleteHostDatumById(hostId) + datastore.DStore().Store() + + return h +} diff --git a/pkg/manager/port.go b/pkg/manager/port.go index c99bf65..1940110 100644 --- a/pkg/manager/port.go +++ b/pkg/manager/port.go @@ -35,7 +35,7 @@ type CxlBladePort struct { backendOps backend.BackendOperations } -func NewCxlBladePortById(ctx context.Context, applianceId, bladeId, portId string, ops backend.BackendOperations) (*CxlBladePort) { +func NewCxlBladePortById(ctx context.Context, applianceId, bladeId, portId string, ops backend.BackendOperations) *CxlBladePort { logger := klog.FromContext(ctx) logger.V(4).Info(">>>>>> NewCxlBladePortById: ", "portId", portId, "bladeId", bladeId, "applianceId", applianceId, "backend", ops.GetBackendInfo(ctx).BackendName) From 5380f5a8f58adf72c4edbb8c606eaa0886bcd02e Mon Sep 17 00:00:00 2001 From: Scott Howe Date: Thu, 26 Sep 2024 12:38:49 -0600 Subject: [PATCH 06/12] doc: replace outdated docker command used for interacting with the release package's cfm-cli tool (#33) --- docs/DOCKER.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/docs/DOCKER.md b/docs/DOCKER.md index 7daef1f..ed7d129 100644 --- a/docs/DOCKER.md +++ b/docs/DOCKER.md @@ -38,13 +38,18 @@ docker restart ## Excute CLI tool -The user can start a cfm docker container to use the cli tool to interact with the running cfm-service. +The user can interact with the running cfm docker container (running cfm-service) to utilize the cli tool. ```bash -docker run --network=host --entrypoint "/cfm/cfm-cli" cfm +docker exec -it ./cfm-cli ``` -NOTE: Use \ = "-h" for help +NOTE: cfm-cli \ usage examples: + +```bash +docker exec -it cfm-container ./cfm-cli -h +docker exec -it cfm-container ./cfm-cli list appliances +``` ## Customization From acb0171e07dad2bb1add64a5ce27d17a37632c2e Mon Sep 17 00:00:00 2001 From: Scott Howe Date: Tue, 1 Oct 2024 09:01:28 -0600 Subject: [PATCH 07/12] fix: improve error messaging (#35) fix: implement more detailed cfm-service error messaging to communicate more detailed error information to the client --- cli/pkg/serviceLib/serviceWrap/appliance.go | 110 +++++++---- cli/pkg/serviceLib/serviceWrap/blade.go | 150 ++++++++++----- cli/pkg/serviceLib/serviceWrap/host.go | 110 +++++++---- .../serviceLib/serviceWrap/memory-devices.go | 82 +++++++-- cli/pkg/serviceLib/serviceWrap/memory.go | 171 ++++++++++++++---- cli/pkg/serviceLib/serviceWrap/memoryBlade.go | 17 +- cli/pkg/serviceLib/serviceWrap/ports.go | 128 +++++++++---- cli/pkg/serviceLib/serviceWrap/resources.go | 56 ++++-- 8 files changed, 609 insertions(+), 215 deletions(-) diff --git a/cli/pkg/serviceLib/serviceWrap/appliance.go b/cli/pkg/serviceLib/serviceWrap/appliance.go index 3b707d7..e4956f9 100644 --- a/cli/pkg/serviceLib/serviceWrap/appliance.go +++ b/cli/pkg/serviceLib/serviceWrap/appliance.go @@ -4,6 +4,7 @@ package serviceWrap import ( "context" + "encoding/json" "fmt" service "cfm/pkg/client" @@ -12,64 +13,100 @@ import ( ) func AddAppliance(client *service.APIClient, creds *service.Credentials) (*service.Appliance, error) { - newReqAddAppl := client.DefaultAPI.AppliancesPost(context.Background()) - newReqAddAppl = newReqAddAppl.Credentials(*creds) - addedAppl, response, err := newReqAddAppl.Execute() + request := client.DefaultAPI.AppliancesPost(context.Background()) + request = request.Credentials(*creds) + appliance, response, err := request.Execute() if err != nil { - msg := fmt.Sprintf("%T: Execute FAILURE", newReqAddAppl) - klog.ErrorS(err, msg, "response", response) - return nil, fmt.Errorf("failure: appliances post: %s", err) + // Decode the JSON response into a struct + var status service.StatusMessage + if err := json.NewDecoder(response.Body).Decode(&status); err != nil { + newErr := fmt.Errorf("failure: Execute(%T): err(%s), error decoding response JSON", request, err) + klog.V(4).Info(newErr) + return nil, newErr + } + + newErr := fmt.Errorf("failure: Execute(%T): err(%s), uri(%s), details(%s), code(%d), message(%s)", + request, err, status.Uri, status.Details, status.Status.Code, status.Status.Message) + klog.V(4).Info(newErr) + return nil, newErr } - klog.V(3).InfoS("AppliancesPost success", "ID", addedAppl.GetId()) + klog.V(3).InfoS("success: AddAppliance", "applianceId", appliance.GetId()) - return addedAppl, nil + return appliance, nil } func DeleteApplianceById(client *service.APIClient, applId string) (*service.Appliance, error) { - newReqDelApplById := client.DefaultAPI.AppliancesDeleteById(context.Background(), applId) - deletedAppl, response, err := newReqDelApplById.Execute() + request := client.DefaultAPI.AppliancesDeleteById(context.Background(), applId) + appliance, response, err := request.Execute() if err != nil { - msg := fmt.Sprintf("%T: Execute FAILURE", newReqDelApplById) - klog.ErrorS(err, msg, "response", response) - return nil, fmt.Errorf("failure: delete appliance by id failure") + // Decode the JSON response into a struct + var status service.StatusMessage + if err := json.NewDecoder(response.Body).Decode(&status); err != nil { + newErr := fmt.Errorf("failure: Execute(%T): err(%s), error decoding response JSON", request, err) + klog.V(4).Info(newErr) + return nil, newErr + } + + newErr := fmt.Errorf("failure: Execute(%T): err(%s), uri(%s), details(%s), code(%d), message(%s)", + request, err, status.Uri, status.Details, status.Status.Code, status.Status.Message) + klog.V(4).Info(newErr) + return nil, newErr } - klog.V(3).InfoS("AppliancesDeleteById success", "ID", deletedAppl.GetId()) + klog.V(3).InfoS("success: DeleteApplianceById", "applianceId", appliance.GetId()) - return deletedAppl, nil + return appliance, nil } func GetAllAppliances(client *service.APIClient) (*[]*service.Appliance, error) { var appliances []*service.Appliance //Get existing appliances - newReqGetAppls := client.DefaultAPI.AppliancesGet(context.Background()) - collection, response, err := newReqGetAppls.Execute() + requestGetAppls := client.DefaultAPI.AppliancesGet(context.Background()) + collection, response, err := requestGetAppls.Execute() if err != nil { - msg := fmt.Sprintf("%T: Execute FAILURE", newReqGetAppls) - klog.ErrorS(err, msg, "response", response) - return nil, fmt.Errorf("failure: appliances get: %s", err) + // Decode the JSON response into a struct + var status service.StatusMessage + if err := json.NewDecoder(response.Body).Decode(&status); err != nil { + newErr := fmt.Errorf("failure: Execute(%T): err(%s), error decoding response JSON", requestGetAppls, err) + klog.V(4).Info(newErr) + return nil, newErr + } + + newErr := fmt.Errorf("failure: Execute(%T): err(%s), uri(%s), details(%s), code(%d), message(%s)", + requestGetAppls, err, status.Uri, status.Details, status.Status.Code, status.Status.Message) + klog.V(4).Info(newErr) + return nil, newErr } - klog.V(3).InfoS("AppliancesGet success", "count", collection.GetMemberCount()) + klog.V(4).InfoS("success: AppliancesGet", "count", collection.GetMemberCount()) for _, member := range collection.GetMembers() { id := ReadLastItemFromUri(member.GetUri()) - newReqGetApplById := client.DefaultAPI.AppliancesGetById(context.Background(), id) - appliance, response, err := newReqGetApplById.Execute() + requestGetApplById := client.DefaultAPI.AppliancesGetById(context.Background(), id) + appliance, response, err := requestGetApplById.Execute() if err != nil { - msg := fmt.Sprintf("%T: Execute FAILURE", newReqGetApplById) - klog.ErrorS(err, msg, "response", response) - return nil, fmt.Errorf("failure: appliances get by id: %s", err) + // Decode the JSON response into a struct + var status service.StatusMessage + if err := json.NewDecoder(response.Body).Decode(&status); err != nil { + newErr := fmt.Errorf("failure: Execute(%T): err(%s), error decoding response JSON", requestGetApplById, err) + klog.V(4).Info(newErr) + return nil, newErr + } + + newErr := fmt.Errorf("failure: Execute(%T): err(%s), uri(%s), details(%s), code(%d), message(%s)", + requestGetApplById, err, status.Uri, status.Details, status.Status.Code, status.Status.Message) + klog.V(4).Info(newErr) + return nil, newErr } - klog.V(3).InfoS("AppliancesGetById success", "ID", appliance.GetId()) + klog.V(4).InfoS("success: AppliancesGetById", "applianceId", appliance.GetId()) appliances = append(appliances, appliance) } - klog.V(4).InfoS("Discovered appliances", "count", len(appliances)) + klog.V(3).InfoS("success: GetAllAppliances", "count", len(appliances)) return &appliances, nil } @@ -78,12 +115,21 @@ func ResyncApplianceById(client *service.APIClient, applianceId string) (*servic request := client.DefaultAPI.AppliancesResyncById(context.Background(), applianceId) appliance, response, err := request.Execute() if err != nil { - msg := fmt.Sprintf("%T: Execute FAILURE", appliance) - klog.ErrorS(err, msg, "response", response) - return nil, fmt.Errorf("failure: resync appliance by id failure") + // Decode the JSON response into a struct + var status service.StatusMessage + if err := json.NewDecoder(response.Body).Decode(&status); err != nil { + newErr := fmt.Errorf("failure: Execute(%T): err(%s), error decoding response JSON", request, err) + klog.V(4).Info(newErr) + return nil, newErr + } + + newErr := fmt.Errorf("failure: Execute(%T): err(%s), uri(%s), details(%s), code(%d), message(%s)", + request, err, status.Uri, status.Details, status.Status.Code, status.Status.Message) + klog.V(4).Info(newErr) + return nil, newErr } - klog.V(3).InfoS("BladesResyncById success", "applianceId", appliance.GetId()) + klog.V(3).InfoS("success: ResyncApplianceById", "applianceId", appliance.GetId()) return appliance, nil } diff --git a/cli/pkg/serviceLib/serviceWrap/blade.go b/cli/pkg/serviceLib/serviceWrap/blade.go index 3c86aaa..72ed691 100644 --- a/cli/pkg/serviceLib/serviceWrap/blade.go +++ b/cli/pkg/serviceLib/serviceWrap/blade.go @@ -4,6 +4,7 @@ package serviceWrap import ( "context" + "encoding/json" "fmt" service "cfm/pkg/client" @@ -50,47 +51,74 @@ func (s *ApplianceBladeSummary) ApplianceCount() int { } func AddBlade(client *service.APIClient, applianceId string, bladeCreds *service.Credentials) (*service.Blade, error) { - addRequest := client.DefaultAPI.BladesPost(context.Background(), applianceId) - addRequest = addRequest.Credentials(*bladeCreds) - addedBlade, response, err := addRequest.Execute() + request := client.DefaultAPI.BladesPost(context.Background(), applianceId) + request = request.Credentials(*bladeCreds) + blade, response, err := request.Execute() if err != nil { - msg := fmt.Sprintf("%T: Execute FAILURE", addRequest) - klog.ErrorS(err, msg, "response", response) - return nil, fmt.Errorf("failure: blades post: %s", err) + // Decode the JSON response into a struct + var status service.StatusMessage + if err := json.NewDecoder(response.Body).Decode(&status); err != nil { + newErr := fmt.Errorf("failure: Execute(%T): err(%s), error decoding response JSON", request, err) + klog.V(4).Info(newErr) + return nil, newErr + } + + newErr := fmt.Errorf("failure: Execute(%T): err(%s), uri(%s), details(%s), code(%d), message(%s)", + request, err, status.Uri, status.Details, status.Status.Code, status.Status.Message) + klog.V(4).Info(newErr) + return nil, newErr } - klog.V(3).InfoS("BladesPost success", "bladeId", addedBlade.GetId(), "applianceId", applianceId) + klog.V(3).InfoS("success: AddBlade", "bladeId", blade.GetId(), "applianceId", applianceId) - return addedBlade, nil + return blade, nil } func DeleteBladeById(client *service.APIClient, applId, bladeId string) (*service.Blade, error) { - deleteRequest := client.DefaultAPI.BladesDeleteById(context.Background(), applId, bladeId) - deletedBlade, response, err := deleteRequest.Execute() + request := client.DefaultAPI.BladesDeleteById(context.Background(), applId, bladeId) + blade, response, err := request.Execute() if err != nil { - msg := fmt.Sprintf("%T: Execute FAILURE", deleteRequest) - klog.ErrorS(err, msg, "response", response) - return nil, fmt.Errorf("failure: delete blade by id failure") + // Decode the JSON response into a struct + var status service.StatusMessage + if err := json.NewDecoder(response.Body).Decode(&status); err != nil { + newErr := fmt.Errorf("failure: Execute(%T): err(%s), error decoding response JSON", request, err) + klog.V(4).Info(newErr) + return nil, newErr + } + + newErr := fmt.Errorf("failure: Execute(%T): err(%s), uri(%s), details(%s), code(%d), message(%s)", + request, err, status.Uri, status.Details, status.Status.Code, status.Status.Message) + klog.V(4).Info(newErr) + return nil, newErr } - klog.V(3).InfoS("BladesDeleteById success", "ApplianceID", applId, "BladeID", deletedBlade.GetId()) + klog.V(3).InfoS("success: DeleteBladeById", "applianceId", applId, "bladeID", blade.GetId()) - return deletedBlade, nil + return blade, nil } // Find a specific Blade by ID on a specific Appliance func FindBladeById_SingleAppl(client *service.APIClient, applId, bladeId string) (*service.Blade, error) { - requestBlade := client.DefaultAPI.BladesGetById(context.Background(), applId, bladeId) + request := client.DefaultAPI.BladesGetById(context.Background(), applId, bladeId) //TODO: What does this api do when the blade is empty - blade, response, err := requestBlade.Execute() + blade, response, err := request.Execute() if err != nil { - msg := fmt.Sprintf("%T: Execute FAILURE", requestBlade) - klog.ErrorS(err, msg, "response", response) - return nil, fmt.Errorf("failure: get appliance blade by id: %s", err) + // Decode the JSON response into a struct + var status service.StatusMessage + if err := json.NewDecoder(response.Body).Decode(&status); err != nil { + newErr := fmt.Errorf("failure: Execute(%T): err(%s), error decoding response JSON", request, err) + klog.V(4).Info(newErr) + return nil, newErr + } + + newErr := fmt.Errorf("failure: Execute(%T): err(%s), uri(%s), details(%s), code(%d), message(%s)", + request, err, status.Uri, status.Details, status.Status.Code, status.Status.Message) + klog.V(4).Info(newErr) + return nil, newErr } - klog.V(3).InfoS("BladesGetById success", "applId", applId, "bladeId", blade.GetId()) + klog.V(3).InfoS("success: FindBladeById_SingleAppl", "applianceId", applId, "bladeId", blade.GetId()) return blade, nil } @@ -100,15 +128,24 @@ func FindBladeById_AllAppls(client *service.APIClient, bladeId string) (*Applian summary := NewApplianceBladeSummary() //Get all existing appliances - requestAppliances := client.DefaultAPI.AppliancesGet(context.Background()) - applColl, response, err := requestAppliances.Execute() + request := client.DefaultAPI.AppliancesGet(context.Background()) + applColl, response, err := request.Execute() if err != nil { - msg := fmt.Sprintf("%T: Execute FAILURE", requestAppliances) - klog.ErrorS(err, msg, "response", response) - return nil, fmt.Errorf("failure: get appliances: %s", err) + // Decode the JSON response into a struct + var status service.StatusMessage + if err := json.NewDecoder(response.Body).Decode(&status); err != nil { + newErr := fmt.Errorf("failure: Execute(%T): err(%s), error decoding response JSON", request, err) + klog.V(4).Info(newErr) + return nil, newErr + } + + newErr := fmt.Errorf("failure: Execute(%T): err(%s), uri(%s), details(%s), code(%d), message(%s)", + request, err, status.Uri, status.Details, status.Status.Code, status.Status.Message) + klog.V(4).Info(newErr) + return nil, newErr } - klog.V(3).InfoS("AppliancesGet success", "applCount", applColl.GetMemberCount()) + klog.V(4).InfoS("success: AppliancesGet", "applCount", applColl.GetMemberCount()) if applColl.GetMemberCount() == 0 { klog.V(3).InfoS("FindBladeById_AllAppls: no appliances found") @@ -134,15 +171,24 @@ func FindBladeById_AllAppls(client *service.APIClient, bladeId string) (*Applian func GetAllBlades_SingleAppl(client *service.APIClient, applId string) (*[]*service.Blade, error) { var blades []*service.Blade - requestBlades := client.DefaultAPI.BladesGet(context.Background(), applId) - bladeColl, response, err := requestBlades.Execute() + request := client.DefaultAPI.BladesGet(context.Background(), applId) + bladeColl, response, err := request.Execute() if err != nil { - msg := fmt.Sprintf("%T: Execute FAILURE", requestBlades) - klog.ErrorS(err, msg, "response", response) - return nil, fmt.Errorf("failure: get appliance blades: %s", err) + // Decode the JSON response into a struct + var status service.StatusMessage + if err := json.NewDecoder(response.Body).Decode(&status); err != nil { + newErr := fmt.Errorf("failure: Execute(%T): err(%s), error decoding response JSON", request, err) + klog.V(4).Info(newErr) + return nil, newErr + } + + newErr := fmt.Errorf("failure: Execute(%T): err(%s), uri(%s), details(%s), code(%d), message(%s)", + request, err, status.Uri, status.Details, status.Status.Code, status.Status.Message) + klog.V(4).Info(newErr) + return nil, newErr } - klog.V(3).InfoS("BladesGet success", "applId", applId, "bladeColl", bladeColl.GetMemberCount()) + klog.V(4).InfoS("success: BladesGet", "applianceId", applId, "bladeCount", bladeColl.GetMemberCount()) for _, bladeMember := range bladeColl.GetMembers() { bladeId := ReadLastItemFromUri(bladeMember.GetUri()) @@ -163,15 +209,24 @@ func GetAllBlades_AllAppls(client *service.APIClient) (*ApplianceBladeSummary, e summary := NewApplianceBladeSummary() //Get all existing appliances - requestAppliances := client.DefaultAPI.AppliancesGet(context.Background()) - applColl, response, err := requestAppliances.Execute() + request := client.DefaultAPI.AppliancesGet(context.Background()) + applColl, response, err := request.Execute() if err != nil { - msg := fmt.Sprintf("%T: Execute FAILURE", requestAppliances) - klog.ErrorS(err, msg, "response", response) - return nil, fmt.Errorf("failure: get appliances: %s", err) + // Decode the JSON response into a struct + var status service.StatusMessage + if err := json.NewDecoder(response.Body).Decode(&status); err != nil { + newErr := fmt.Errorf("failure: Execute(%T): err(%s), error decoding response JSON", request, err) + klog.V(4).Info(newErr) + return nil, newErr + } + + newErr := fmt.Errorf("failure: Execute(%T): err(%s), uri(%s), details(%s), code(%d), message(%s)", + request, err, status.Uri, status.Details, status.Status.Code, status.Status.Message) + klog.V(4).Info(newErr) + return nil, newErr } - klog.V(3).InfoS("AppliancesGet success", "applCount", applColl.GetMemberCount()) + klog.V(4).InfoS("success: AppliancesGet", "applCount", applColl.GetMemberCount()) //Scan collection members for target appliance id for _, appl := range applColl.GetMembers() { @@ -192,12 +247,21 @@ func ResyncBladeById(client *service.APIClient, applId, bladeId string) (*servic request := client.DefaultAPI.BladesResyncById(context.Background(), applId, bladeId) blade, response, err := request.Execute() if err != nil { - msg := fmt.Sprintf("%T: Execute FAILURE", blade) - klog.ErrorS(err, msg, "response", response) - return nil, fmt.Errorf("failure: resync blade by id failure") + // Decode the JSON response into a struct + var status service.StatusMessage + if err := json.NewDecoder(response.Body).Decode(&status); err != nil { + newErr := fmt.Errorf("failure: Execute(%T): err(%s), error decoding response JSON", request, err) + klog.V(4).Info(newErr) + return nil, newErr + } + + newErr := fmt.Errorf("failure: Execute(%T): err(%s), uri(%s), details(%s), code(%d), message(%s)", + request, err, status.Uri, status.Details, status.Status.Code, status.Status.Message) + klog.V(4).Info(newErr) + return nil, newErr } - klog.V(3).InfoS("BladesResyncById success", "ApplianceID", applId, "BladeID", blade.GetId()) + klog.V(3).InfoS("success: ResyncBladeById", "applianceId", applId, "bladeID", blade.GetId()) return blade, nil } diff --git a/cli/pkg/serviceLib/serviceWrap/host.go b/cli/pkg/serviceLib/serviceWrap/host.go index 53ccc2d..8740bd2 100644 --- a/cli/pkg/serviceLib/serviceWrap/host.go +++ b/cli/pkg/serviceLib/serviceWrap/host.go @@ -4,6 +4,7 @@ package serviceWrap import ( "context" + "encoding/json" "fmt" service "cfm/pkg/client" @@ -12,64 +13,100 @@ import ( ) func AddHost(client *service.APIClient, creds *service.Credentials) (*service.Host, error) { - newReqAddHost := client.DefaultAPI.HostsPost(context.Background()) - newReqAddHost = newReqAddHost.Credentials(*creds) - addedHost, response, err := newReqAddHost.Execute() + request := client.DefaultAPI.HostsPost(context.Background()) + request = request.Credentials(*creds) + host, response, err := request.Execute() if err != nil { - msg := fmt.Sprintf("%T: Execute FAILURE", newReqAddHost) - klog.ErrorS(err, msg, "response", response) - return nil, fmt.Errorf("failure: hosts post: %s", err) + // Decode the JSON response into a struct + var status service.StatusMessage + if err := json.NewDecoder(response.Body).Decode(&status); err != nil { + newErr := fmt.Errorf("failure: Execute(%T): err(%s), error decoding response JSON", request, err) + klog.V(4).Info(newErr) + return nil, newErr + } + + newErr := fmt.Errorf("failure: Execute(%T): err(%s), uri(%s), details(%s), code(%d), message(%s)", + request, err, status.Uri, status.Details, status.Status.Code, status.Status.Message) + klog.V(4).Info(newErr) + return nil, newErr } - klog.V(3).InfoS("HostsPost success", "hostId", addedHost.GetId()) + klog.V(3).InfoS("success: AddHost", "hostId", host.GetId()) - return addedHost, nil + return host, nil } func DeleteHostById(client *service.APIClient, hostId string) (*service.Host, error) { - newReqDelHostById := client.DefaultAPI.HostsDeleteById(context.Background(), hostId) - deletedHost, response, err := newReqDelHostById.Execute() + request := client.DefaultAPI.HostsDeleteById(context.Background(), hostId) + host, response, err := request.Execute() if err != nil { - msg := fmt.Sprintf("%T: Execute FAILURE", newReqDelHostById) - klog.ErrorS(err, msg, "response", response) - return nil, fmt.Errorf("failure: delete host by id failure") + // Decode the JSON response into a struct + var status service.StatusMessage + if err := json.NewDecoder(response.Body).Decode(&status); err != nil { + newErr := fmt.Errorf("failure: Execute(%T): err(%s), error decoding response JSON", request, err) + klog.V(4).Info(newErr) + return nil, newErr + } + + newErr := fmt.Errorf("failure: Execute(%T): err(%s), uri(%s), details(%s), code(%d), message(%s)", + request, err, status.Uri, status.Details, status.Status.Code, status.Status.Message) + klog.V(4).Info(newErr) + return nil, newErr } - klog.V(3).InfoS("HostsDeleteById success", "hostId", deletedHost.GetId()) + klog.V(3).InfoS("success: DeleteHostById", "hostId", host.GetId()) - return deletedHost, nil + return host, nil } func GetAllHosts(client *service.APIClient) (*[]*service.Host, error) { var hosts []*service.Host //Get existing hosts - newReqGetHosts := client.DefaultAPI.HostsGet(context.Background()) - collection, response, err := newReqGetHosts.Execute() + requestGetHosts := client.DefaultAPI.HostsGet(context.Background()) + collection, response, err := requestGetHosts.Execute() if err != nil { - msg := fmt.Sprintf("%T: Execute FAILURE", newReqGetHosts) - klog.ErrorS(err, msg, "response", response) - return nil, fmt.Errorf("failure: hosts get: %s", err) + // Decode the JSON response into a struct + var status service.StatusMessage + if err := json.NewDecoder(response.Body).Decode(&status); err != nil { + newErr := fmt.Errorf("failure: Execute(%T): err(%s), error decoding response JSON", requestGetHosts, err) + klog.V(4).Info(newErr) + return nil, newErr + } + + newErr := fmt.Errorf("failure: Execute(%T): err(%s), uri(%s), details(%s), code(%d), message(%s)", + requestGetHosts, err, status.Uri, status.Details, status.Status.Code, status.Status.Message) + klog.V(4).Info(newErr) + return nil, newErr } - klog.V(3).InfoS("HostsGet success", "count", collection.GetMemberCount()) + klog.V(4).InfoS("success: HostsGet", "count", collection.GetMemberCount()) for _, member := range collection.GetMembers() { id := ReadLastItemFromUri(member.GetUri()) - newReqGetHostById := client.DefaultAPI.HostsGetById(context.Background(), id) - host, response, err := newReqGetHostById.Execute() + requestGetHostById := client.DefaultAPI.HostsGetById(context.Background(), id) + host, response, err := requestGetHostById.Execute() if err != nil { - msg := fmt.Sprintf("%T: Execute FAILURE", newReqGetHostById) - klog.ErrorS(err, msg, "response", response) - return nil, fmt.Errorf("failure: hosts get by id: %s", err) + // Decode the JSON response into a struct + var status service.StatusMessage + if err := json.NewDecoder(response.Body).Decode(&status); err != nil { + newErr := fmt.Errorf("failure: Execute(%T): err(%s), error decoding response JSON", requestGetHostById, err) + klog.V(4).Info(newErr) + return nil, newErr + } + + newErr := fmt.Errorf("failure: Execute(%T): err(%s), uri(%s), details(%s), code(%d), message(%s)", + requestGetHostById, err, status.Uri, status.Details, status.Status.Code, status.Status.Message) + klog.V(4).Info(newErr) + return nil, newErr } - klog.V(3).InfoS("HostsGetById success", "hostId", host.GetId()) + klog.V(4).InfoS("success: HostsGetById", "hostId", host.GetId()) hosts = append(hosts, host) } - klog.V(3).InfoS("Discovered hosts", "count", len(hosts)) + klog.V(3).InfoS("success: GetAllHosts", "Host Count", len(hosts)) return &hosts, nil } @@ -78,12 +115,21 @@ func ResyncHostById(client *service.APIClient, hostId string) (*service.Host, er request := client.DefaultAPI.HostsResyncById(context.Background(), hostId) host, response, err := request.Execute() if err != nil { - msg := fmt.Sprintf("%T: Execute FAILURE", host) - klog.ErrorS(err, msg, "response", response) - return nil, fmt.Errorf("failure: resync host by id failure") + // Decode the JSON response into a struct + var status service.StatusMessage + if err := json.NewDecoder(response.Body).Decode(&status); err != nil { + newErr := fmt.Errorf("failure: Execute(%T): err(%s), error decoding response JSON", request, err) + klog.V(4).Info(newErr) + return nil, newErr + } + + newErr := fmt.Errorf("failure: Execute(%T): err(%s), uri(%s), details(%s), code(%d), message(%s)", + request, err, status.Uri, status.Details, status.Status.Code, status.Status.Message) + klog.V(4).Info(newErr) + return nil, newErr } - klog.V(3).InfoS("BladesResyncById success", "hostID", host.GetId()) + klog.V(3).InfoS("success: ResyncHostById", "hostID", host.GetId()) return host, nil } diff --git a/cli/pkg/serviceLib/serviceWrap/memory-devices.go b/cli/pkg/serviceLib/serviceWrap/memory-devices.go index db829cb..486a2f0 100644 --- a/cli/pkg/serviceLib/serviceWrap/memory-devices.go +++ b/cli/pkg/serviceLib/serviceWrap/memory-devices.go @@ -4,6 +4,7 @@ package serviceWrap import ( "context" + "encoding/json" "fmt" service "cfm/pkg/client" @@ -57,12 +58,21 @@ func FindMemoryDeviceOnHost(client *service.APIClient, hostId, memoryDeviceId st request := client.DefaultAPI.HostsGetMemoryDeviceById(context.Background(), hostId, memoryDeviceId) memoryDevice, response, err := request.Execute() if err != nil { - msg := fmt.Sprintf("%T: Execute FAILURE", request) - klog.ErrorS(err, msg, "response", response) - return nil, fmt.Errorf("failure: get host memoryDevice by id: %s", err) + // Decode the JSON response into a struct + var status service.StatusMessage + if err := json.NewDecoder(response.Body).Decode(&status); err != nil { + newErr := fmt.Errorf("failure: Execute(%T): err(%s), error decoding response JSON", request, err) + klog.V(4).Info(newErr) + return nil, newErr + } + + newErr := fmt.Errorf("failure: Execute(%T): err(%s), uri(%s), details(%s), code(%d), message(%s)", + request, err, status.Uri, status.Details, status.Status.Code, status.Status.Message) + klog.V(4).Info(newErr) + return nil, newErr } - klog.V(3).InfoS("HostsGetMemoryById success", "hostId", hostId, "memoryDeviceId", memoryDevice.GetId()) + klog.V(3).InfoS("success: FindMemoryDeviceOnHost", "hostId", hostId, "memoryDeviceId", memoryDevice.GetId()) return memoryDevice, nil } @@ -73,26 +83,44 @@ func GetAllMemoryDevicesForHost(client *service.APIClient, hostId string) (*[]*s request := client.DefaultAPI.HostsGetMemoryDevices(context.Background(), hostId) memoryDeviceColl, response, err := request.Execute() if err != nil { - msg := fmt.Sprintf("%T: Execute FAILURE", request) - klog.ErrorS(err, msg, "response", response) - // return nil, fmt.Errorf("failure: get host memoryDevices: %s", err) + // Decode the JSON response into a struct + var status service.StatusMessage + if err := json.NewDecoder(response.Body).Decode(&status); err != nil { + newErr := fmt.Errorf("failure: Execute(%T): err(%s), error decoding response JSON", request, err) + klog.V(4).Info(newErr) + return nil, newErr + } + + newErr := fmt.Errorf("failure: Execute(%T): err(%s), uri(%s), details(%s), code(%d), message(%s)", + request, err, status.Uri, status.Details, status.Status.Code, status.Status.Message) + klog.V(4).Info(newErr) + // return nil, newErr return &memoryDevices, nil //TODO: Error here instead? } - klog.V(3).InfoS("HostGetMemory success", "hostId", hostId, "memoryDeviceColl", memoryDeviceColl.GetMemberCount()) + klog.V(4).InfoS("success: HostsGetMemoryDevices", "hostId", hostId, "memoryDeviceColl", memoryDeviceColl.GetMemberCount()) for _, member := range memoryDeviceColl.GetMembers() { memoryDeviceId := ReadLastItemFromUri(member.GetUri()) request := client.DefaultAPI.HostsGetMemoryDeviceById(context.Background(), hostId, memoryDeviceId) memoryDevice, response, err := request.Execute() if err != nil { - msg := fmt.Sprintf("%T: Execute FAILURE", request) - klog.ErrorS(err, msg, "response", response) - // return nil, fmt.Errorf("failure: get host memoryDevice by id: %s", err) + // Decode the JSON response into a struct + var status service.StatusMessage + if err := json.NewDecoder(response.Body).Decode(&status); err != nil { + newErr := fmt.Errorf("failure: Execute(%T): err(%s), error decoding response JSON", request, err) + klog.V(4).Info(newErr) + return nil, newErr + } + + newErr := fmt.Errorf("failure: Execute(%T): err(%s), uri(%s), details(%s), code(%d), message(%s)", + request, err, status.Uri, status.Details, status.Status.Code, status.Status.Message) + klog.V(4).Info(newErr) + // return nil, newErr continue //TODO: Error here instead? } - klog.V(3).InfoS("HostsGetMemoryById success", "hostId", hostId, "memoryDeviceId", memoryDevice.GetId()) + klog.V(4).InfoS("success: HostsGetMemoryById", "hostId", hostId, "memoryDeviceId", memoryDevice.GetId()) memoryDevices = append(memoryDevices, memoryDevice) } @@ -107,9 +135,18 @@ func GetMemoryDevices_AllHosts(client *service.APIClient) (*HostMemoryDeviceSumm request := client.DefaultAPI.HostsGet(context.Background()) hostColl, response, err := request.Execute() if err != nil { - msg := fmt.Sprintf("%T: Execute FAILURE", request) - klog.ErrorS(err, msg, "response", response) - // return nil, fmt.Errorf("failure: get host memoryDevices: %s", err) + // Decode the JSON response into a struct + var status service.StatusMessage + if err := json.NewDecoder(response.Body).Decode(&status); err != nil { + newErr := fmt.Errorf("failure: Execute(%T): err(%s), error decoding response JSON", request, err) + klog.V(4).Info(newErr) + return nil, newErr + } + + newErr := fmt.Errorf("failure: Execute(%T): err(%s), uri(%s), details(%s), code(%d), message(%s)", + request, err, status.Uri, status.Details, status.Status.Code, status.Status.Message) + klog.V(4).Info(newErr) + // return nil, newErr return summary, nil //TODO: Error here instead? } @@ -134,9 +171,18 @@ func FindMemoryDevice_AllHosts(client *service.APIClient, memoryDeviceId string) request := client.DefaultAPI.HostsGet(context.Background()) hostColl, response, err := request.Execute() if err != nil { - msg := fmt.Sprintf("%T: Execute FAILURE", request) - klog.ErrorS(err, msg, "response", response) - // return nil, fmt.Errorf("failure: get host memoryDevices: %s", err) + // Decode the JSON response into a struct + var status service.StatusMessage + if err := json.NewDecoder(response.Body).Decode(&status); err != nil { + newErr := fmt.Errorf("failure: Execute(%T): err(%s), error decoding response JSON", request, err) + klog.V(4).Info(newErr) + return nil, newErr + } + + newErr := fmt.Errorf("failure: Execute(%T): err(%s), uri(%s), details(%s), code(%d), message(%s)", + request, err, status.Uri, status.Details, status.Status.Code, status.Status.Message) + klog.V(4).Info(newErr) + // return nil, newErr return summary, nil //TODO: Error here instead? } diff --git a/cli/pkg/serviceLib/serviceWrap/memory.go b/cli/pkg/serviceLib/serviceWrap/memory.go index f9d374f..48ca48a 100644 --- a/cli/pkg/serviceLib/serviceWrap/memory.go +++ b/cli/pkg/serviceLib/serviceWrap/memory.go @@ -4,6 +4,7 @@ package serviceWrap import ( "context" + "encoding/json" "fmt" service "cfm/pkg/client" @@ -53,15 +54,24 @@ func (s *BladeMemoryRegionSummary) AddMemoryRegionSlice(applId, bladeId string, func FindMemoryRegionOnBlade(client *service.APIClient, applId, bladeId, memoryId string) (*service.MemoryRegion, error) { var memoryRegion *service.MemoryRegion - requestMemoryRegionById := client.DefaultAPI.BladesGetMemoryById(context.Background(), applId, bladeId, memoryId) - memoryRegion, response, err := requestMemoryRegionById.Execute() + request := client.DefaultAPI.BladesGetMemoryById(context.Background(), applId, bladeId, memoryId) + memoryRegion, response, err := request.Execute() if err != nil { - msg := fmt.Sprintf("%T: Execute FAILURE", requestMemoryRegionById) - klog.ErrorS(err, msg, "response", response) - return nil, fmt.Errorf("failure: get appliance blade memoryRegion by id: %s", err) + // Decode the JSON response into a struct + var status service.StatusMessage + if err := json.NewDecoder(response.Body).Decode(&status); err != nil { + newErr := fmt.Errorf("failure: Execute(%T): err(%s), error decoding response JSON", request, err) + klog.V(4).Info(newErr) + return nil, newErr + } + + newErr := fmt.Errorf("failure: Execute(%T): err(%s), uri(%s), details(%s), code(%d), message(%s)", + request, err, status.Uri, status.Details, status.Status.Code, status.Status.Message) + klog.V(4).Info(newErr) + return nil, newErr } - klog.V(3).InfoS("BladesGetMemoryRegionById success", "applId", applId, "bladeId", bladeId, "memoryId", memoryRegion.GetId()) + klog.V(3).InfoS("success: FindMemoryRegionOnBlade", "applId", applId, "bladeId", bladeId, "memoryId", memoryRegion.GetId()) return memoryRegion, nil } @@ -72,26 +82,44 @@ func GetAllMemoryRegionsForBlade(client *service.APIClient, applId, bladeId stri requestMemoryRegions := client.DefaultAPI.BladesGetMemory(context.Background(), applId, bladeId) memoryRegionColl, response, err := requestMemoryRegions.Execute() if err != nil { - msg := fmt.Sprintf("%T: Execute FAILURE", requestMemoryRegions) - klog.ErrorS(err, msg, "response", response) - // return nil, fmt.Errorf("failure: get appliance blade memoryRegions: %s", err) + // Decode the JSON response into a struct + var status service.StatusMessage + if err := json.NewDecoder(response.Body).Decode(&status); err != nil { + newErr := fmt.Errorf("failure: Execute(%T): err(%s), error decoding response JSON", requestMemoryRegions, err) + klog.V(4).Info(newErr) + return nil, newErr + } + + newErr := fmt.Errorf("failure: Execute(%T): err(%s), uri(%s), details(%s), code(%d), message(%s)", + requestMemoryRegions, err, status.Uri, status.Details, status.Status.Code, status.Status.Message) + klog.V(4).Info(newErr) + // return nil, newErr return &memoryRegions, nil //TODO: Error here instead? } - klog.V(3).InfoS("BladesGetMemoryRegions success", "applId", applId, "bladeId", bladeId, "memoryRegionColl", memoryRegionColl.GetMemberCount()) + klog.V(4).InfoS("success: BladesGetMemory", "applId", applId, "bladeId", bladeId, "memoryRegionColl", memoryRegionColl.GetMemberCount()) for _, res := range memoryRegionColl.GetMembers() { memoryId := ReadLastItemFromUri(res.GetUri()) requestMemoryRegionById := client.DefaultAPI.BladesGetMemoryById(context.Background(), applId, bladeId, memoryId) memoryRegion, response, err := requestMemoryRegionById.Execute() if err != nil { - msg := fmt.Sprintf("%T: Execute FAILURE", requestMemoryRegionById) - klog.ErrorS(err, msg, "response", response) - // return nil, fmt.Errorf("failure: get appliance blade memoryRegion by id: %s", err) + // Decode the JSON response into a struct + var status service.StatusMessage + if err := json.NewDecoder(response.Body).Decode(&status); err != nil { + newErr := fmt.Errorf("failure: Execute(%T): err(%s), error decoding response JSON", requestMemoryRegionById, err) + klog.V(4).Info(newErr) + return nil, newErr + } + + newErr := fmt.Errorf("failure: Execute(%T): err(%s), uri(%s), details(%s), code(%d), message(%s)", + requestMemoryRegionById, err, status.Uri, status.Details, status.Status.Code, status.Status.Message) + klog.V(4).Info(newErr) + // return nil, newErr continue //TODO: Error here instead? } - klog.V(3).InfoS("BladesGetMemoryRegionById success", "applId", applId, "bladeId", bladeId, "memoryId", memoryRegion.GetId()) + klog.V(4).InfoS("success: BladesGetMemoryById", "applId", applId, "bladeId", bladeId, "memoryId", memoryRegion.GetId()) memoryRegions = append(memoryRegions, memoryRegion) } @@ -301,12 +329,21 @@ func ComposeMemory(client *service.APIClient, applianceId, bladeId, portId strin // Execute ApiAppliancesComposeMemoryRequest region, response, err := requestApiCompose.Execute() if err != nil { - msg := fmt.Sprintf("%T: Execute FAILURE", requestApiCompose) - klog.ErrorS(err, msg, "response", response) - return nil, fmt.Errorf("failure: appliance blade compose memory: %s", err) + // Decode the JSON response into a struct + var status service.StatusMessage + if err := json.NewDecoder(response.Body).Decode(&status); err != nil { + newErr := fmt.Errorf("failure: Execute(%T): err(%s), error decoding response JSON", requestApiCompose, err) + klog.V(4).Info(newErr) + return nil, newErr + } + + newErr := fmt.Errorf("failure: Execute(%T): err(%s), uri(%s), details(%s), code(%d), message(%s)", + requestApiCompose, err, status.Uri, status.Details, status.Status.Code, status.Status.Message) + klog.V(4).Info(newErr) + return nil, newErr } - klog.V(3).InfoS("BladesComposeMemory success", "region", region.GetId(), "size", region.GetSizeMiB(), "applId", applianceId, "bladeId", bladeId) + klog.V(3).InfoS("success: ComposeMemory", "region", region.GetId(), "size", region.GetSizeMiB(), "applId", applianceId, "bladeId", bladeId) return region, nil } @@ -319,12 +356,21 @@ func FreeMemory(client *service.APIClient, applianceId, bladeId, memoryId string // Execute ApiAppliancesFreeMemoryByIdRequest region, response, err := requestFree.Execute() if err != nil { - msg := fmt.Sprintf("%T: Execute FAILURE", requestFree) - klog.ErrorS(err, msg, "response", response) - return nil, fmt.Errorf("failure: appliances free memory: %s", err) + // Decode the JSON response into a struct + var status service.StatusMessage + if err := json.NewDecoder(response.Body).Decode(&status); err != nil { + newErr := fmt.Errorf("failure: Execute(%T): err(%s), error decoding response JSON", requestFree, err) + klog.V(4).Info(newErr) + return nil, newErr + } + + newErr := fmt.Errorf("failure: Execute(%T): err(%s), uri(%s), details(%s), code(%d), message(%s)", + requestFree, err, status.Uri, status.Details, status.Status.Code, status.Status.Message) + klog.V(4).Info(newErr) + return nil, newErr } - klog.V(3).InfoS("BladesFreeMemoryById success", "region", region.GetId(), "size", region.GetSizeMiB(), "applId", applianceId, "bladeId", bladeId) + klog.V(3).InfoS("success: FreeMemory", "region", region.GetId(), "size", region.GetSizeMiB(), "applId", applianceId, "bladeId", bladeId) return region, nil } @@ -378,12 +424,21 @@ func FindMemoryRegionOnHost(client *service.APIClient, hostId, memoryId string) request := client.DefaultAPI.HostsGetMemoryById(context.Background(), hostId, memoryId) memoryRegion, response, err := request.Execute() if err != nil { - msg := fmt.Sprintf("%T: Execute FAILURE", request) - klog.ErrorS(err, msg, "response", response) - return nil, fmt.Errorf("failure: get host memoryRegion by id: %s", err) + // Decode the JSON response into a struct + var status service.StatusMessage + if err := json.NewDecoder(response.Body).Decode(&status); err != nil { + newErr := fmt.Errorf("failure: Execute(%T): err(%s), error decoding response JSON", request, err) + klog.V(4).Info(newErr) + return nil, newErr + } + + newErr := fmt.Errorf("failure: Execute(%T): err(%s), uri(%s), details(%s), code(%d), message(%s)", + request, err, status.Uri, status.Details, status.Status.Code, status.Status.Message) + klog.V(4).Info(newErr) + return nil, newErr } - klog.V(3).InfoS("HostsGetMemoryById success", "hostId", hostId, "memoryId", memoryRegion.GetId()) + klog.V(3).InfoS("success: FindMemoryRegionOnHost", "hostId", hostId, "memoryId", memoryRegion.GetId()) return memoryRegion, nil } @@ -394,26 +449,44 @@ func GetAllMemoryRegionsForHost(client *service.APIClient, hostId string) (*[]*s request := client.DefaultAPI.HostGetMemory(context.Background(), hostId) memoryRegionColl, response, err := request.Execute() if err != nil { - msg := fmt.Sprintf("%T: Execute FAILURE", request) - klog.ErrorS(err, msg, "response", response) - // return nil, fmt.Errorf("failure: get host memoryRegions: %s", err) + // Decode the JSON response into a struct + var status service.StatusMessage + if err := json.NewDecoder(response.Body).Decode(&status); err != nil { + newErr := fmt.Errorf("failure: Execute(%T): err(%s), error decoding response JSON", request, err) + klog.V(4).Info(newErr) + return nil, newErr + } + + newErr := fmt.Errorf("failure: Execute(%T): err(%s), uri(%s), details(%s), code(%d), message(%s)", + request, err, status.Uri, status.Details, status.Status.Code, status.Status.Message) + klog.V(4).Info(newErr) + // return nil, newErr return &memoryRegions, nil //TODO: Error here instead? } - klog.V(3).InfoS("HostGetMemory success", "hostId", hostId, "memoryRegionColl", memoryRegionColl.GetMemberCount()) + klog.V(4).InfoS("success: HostGetMemory", "hostId", hostId, "memoryRegionColl", memoryRegionColl.GetMemberCount()) for _, member := range memoryRegionColl.GetMembers() { memoryId := ReadLastItemFromUri(member.GetUri()) request := client.DefaultAPI.HostsGetMemoryById(context.Background(), hostId, memoryId) memoryRegion, response, err := request.Execute() if err != nil { - msg := fmt.Sprintf("%T: Execute FAILURE", request) - klog.ErrorS(err, msg, "response", response) - // return nil, fmt.Errorf("failure: get host memoryRegion by id: %s", err) + // Decode the JSON response into a struct + var status service.StatusMessage + if err := json.NewDecoder(response.Body).Decode(&status); err != nil { + newErr := fmt.Errorf("failure: Execute(%T): err(%s), error decoding response JSON", request, err) + klog.V(4).Info(newErr) + return nil, newErr + } + + newErr := fmt.Errorf("failure: Execute(%T): err(%s), uri(%s), details(%s), code(%d), message(%s)", + request, err, status.Uri, status.Details, status.Status.Code, status.Status.Message) + klog.V(4).Info(newErr) + // return nil, newErr continue //TODO: Error here instead? } - klog.V(3).InfoS("HostsGetMemoryById success", "hostId", hostId, "memoryId", memoryRegion.GetId()) + klog.V(4).InfoS("success: HostsGetMemoryById", "hostId", hostId, "memoryId", memoryRegion.GetId()) memoryRegions = append(memoryRegions, memoryRegion) } @@ -428,9 +501,18 @@ func GetMemoryRegions_AllHosts(client *service.APIClient) (*HostMemoryRegionSumm request := client.DefaultAPI.HostsGet(context.Background()) hostColl, response, err := request.Execute() if err != nil { - msg := fmt.Sprintf("%T: Execute FAILURE", request) - klog.ErrorS(err, msg, "response", response) - // return nil, fmt.Errorf("failure: get host memoryRegions: %s", err) + // Decode the JSON response into a struct + var status service.StatusMessage + if err := json.NewDecoder(response.Body).Decode(&status); err != nil { + newErr := fmt.Errorf("failure: Execute(%T): err(%s), error decoding response JSON", request, err) + klog.V(4).Info(newErr) + return nil, newErr + } + + newErr := fmt.Errorf("failure: Execute(%T): err(%s), uri(%s), details(%s), code(%d), message(%s)", + request, err, status.Uri, status.Details, status.Status.Code, status.Status.Message) + klog.V(4).Info(newErr) + // return nil, newErr return summary, nil //TODO: Error here instead? } @@ -455,9 +537,18 @@ func FindMemoryRegion_AllHosts(client *service.APIClient, memoryId string) (*Hos request := client.DefaultAPI.HostsGet(context.Background()) hostColl, response, err := request.Execute() if err != nil { - msg := fmt.Sprintf("%T: Execute FAILURE", request) - klog.ErrorS(err, msg, "response", response) - // return nil, fmt.Errorf("failure: get host memoryRegions: %s", err) + // Decode the JSON response into a struct + var status service.StatusMessage + if err := json.NewDecoder(response.Body).Decode(&status); err != nil { + newErr := fmt.Errorf("failure: Execute(%T): err(%s), error decoding response JSON", request, err) + klog.V(4).Info(newErr) + return nil, newErr + } + + newErr := fmt.Errorf("failure: Execute(%T): err(%s), uri(%s), details(%s), code(%d), message(%s)", + request, err, status.Uri, status.Details, status.Status.Code, status.Status.Message) + klog.V(4).Info(newErr) + // return nil, newErr return summary, nil //TODO: Error here instead? } diff --git a/cli/pkg/serviceLib/serviceWrap/memoryBlade.go b/cli/pkg/serviceLib/serviceWrap/memoryBlade.go index 90603b7..fbaca44 100644 --- a/cli/pkg/serviceLib/serviceWrap/memoryBlade.go +++ b/cli/pkg/serviceLib/serviceWrap/memoryBlade.go @@ -4,6 +4,7 @@ package serviceWrap import ( "context" + "encoding/json" "fmt" service "cfm/pkg/client" @@ -26,13 +27,21 @@ func BladesAssignMemory(client *service.APIClient, applianceId, bladeId, memoryI // Execute ApiBladesAssignMemoryByIdRequest region, response, err := bladeRequest.Execute() if err != nil { - newErr := fmt.Errorf("failure: blade %s memory: %w", operation, err) - msg := fmt.Sprintf("%T: Execute FAILURE", bladeRequest) - klog.ErrorS(newErr, msg, "response", response, "bladeRequest", bladeRequest) + // Decode the JSON response into a struct + var status service.StatusMessage + if err := json.NewDecoder(response.Body).Decode(&status); err != nil { + newErr := fmt.Errorf("failure: Execute(%T): err(%s), error decoding response JSON", bladeRequest, err) + klog.V(4).Info(newErr) + return nil, newErr + } + + newErr := fmt.Errorf("failure: Execute(%T): err(%s), uri(%s), details(%s), code(%d), message(%s)", + bladeRequest, err, status.Uri, status.Details, status.Status.Code, status.Status.Message) + klog.V(4).Info(newErr) return nil, newErr } - klog.V(3).InfoS("BladesAssignMemory success", "operation", operation, "memoryId", region.GetId(), "portId", region.GetMemoryAppliancePort(), "size", region.GetSizeMiB(), "applId", region.GetMemoryApplianceId(), "bladeId", region.GetMemoryBladeId()) + klog.V(3).InfoS("sucess: BladesAssignMemory", "operation", operation, "memoryId", region.GetId(), "portId", region.GetMemoryAppliancePort(), "size", region.GetSizeMiB(), "applId", region.GetMemoryApplianceId(), "bladeId", region.GetMemoryBladeId()) return region, nil } diff --git a/cli/pkg/serviceLib/serviceWrap/ports.go b/cli/pkg/serviceLib/serviceWrap/ports.go index 3fedcc8..982272c 100644 --- a/cli/pkg/serviceLib/serviceWrap/ports.go +++ b/cli/pkg/serviceLib/serviceWrap/ports.go @@ -4,6 +4,7 @@ package serviceWrap import ( "context" + "encoding/json" "fmt" service "cfm/pkg/client" @@ -53,15 +54,24 @@ func (s *BladePortsSummary) AddPortSlice(applId, bladeId string, ports *[]*servi func FindPortOnBlade(client *service.APIClient, applId, bladeId, portId string) (*service.PortInformation, error) { var port *service.PortInformation - requestPortById := client.DefaultAPI.BladesGetPortById(context.Background(), applId, bladeId, portId) - port, response, err := requestPortById.Execute() + request := client.DefaultAPI.BladesGetPortById(context.Background(), applId, bladeId, portId) + port, response, err := request.Execute() if err != nil { - msg := fmt.Sprintf("%T: Execute FAILURE", requestPortById) - klog.ErrorS(err, msg, "response", response) - return nil, fmt.Errorf("failure: get appliance blade port by id: %s", err) + // Decode the JSON response into a struct + var status service.StatusMessage + if err := json.NewDecoder(response.Body).Decode(&status); err != nil { + newErr := fmt.Errorf("failure: Execute(%T): err(%s), error decoding response JSON", request, err) + klog.V(4).Info(newErr) + return nil, newErr + } + + newErr := fmt.Errorf("failure: Execute(%T): err(%s), uri(%s), details(%s), code(%d), message(%s)", + request, err, status.Uri, status.Details, status.Status.Code, status.Status.Message) + klog.V(4).Info(newErr) + return nil, newErr } - klog.V(3).InfoS("BladesGetPortById success", "applId", applId, "bladeId", bladeId, "portId", port.GetId()) + klog.V(3).InfoS("success: FindPortOnBlade", "applId", applId, "bladeId", bladeId, "portId", port.GetId()) return port, nil } @@ -72,26 +82,44 @@ func GetAllPortsForBlade(client *service.APIClient, applId, bladeId string) (*[] requestPorts := client.DefaultAPI.BladesGetPorts(context.Background(), applId, bladeId) portColl, response, err := requestPorts.Execute() if err != nil { - msg := fmt.Sprintf("%T: Execute FAILURE", requestPorts) - klog.ErrorS(err, msg, "response", response) - // return nil, fmt.Errorf("failure: get appliance blade ports: %s", err) + // Decode the JSON response into a struct + var status service.StatusMessage + if err := json.NewDecoder(response.Body).Decode(&status); err != nil { + newErr := fmt.Errorf("failure: Execute(%T): err(%s), error decoding response JSON", requestPorts, err) + klog.V(4).Info(newErr) + return nil, newErr + } + + newErr := fmt.Errorf("failure: Execute(%T): err(%s), uri(%s), details(%s), code(%d), message(%s)", + requestPorts, err, status.Uri, status.Details, status.Status.Code, status.Status.Message) + klog.V(4).Info(newErr) + // return nil, newErr return &ports, nil //TODO: Error here instead? } - klog.V(3).InfoS("BladesGetPorts success", "applId", applId, "bladeId", bladeId, "portColl", portColl.GetMemberCount()) + klog.V(4).InfoS("success: BladesGetPorts", "applId", applId, "bladeId", bladeId, "portColl", portColl.GetMemberCount()) for _, res := range portColl.GetMembers() { portId := ReadLastItemFromUri(res.GetUri()) requestPortById := client.DefaultAPI.BladesGetPortById(context.Background(), applId, bladeId, portId) port, response, err := requestPortById.Execute() if err != nil { - msg := fmt.Sprintf("%T: Execute FAILURE", requestPortById) - klog.ErrorS(err, msg, "response", response) - // return nil, fmt.Errorf("failure: get appliance blade port by id: %s", err) + // Decode the JSON response into a struct + var status service.StatusMessage + if err := json.NewDecoder(response.Body).Decode(&status); err != nil { + newErr := fmt.Errorf("failure: Execute(%T): err(%s), error decoding response JSON", requestPortById, err) + klog.V(4).Info(newErr) + return nil, newErr + } + + newErr := fmt.Errorf("failure: Execute(%T): err(%s), uri(%s), details(%s), code(%d), message(%s)", + requestPortById, err, status.Uri, status.Details, status.Status.Code, status.Status.Message) + klog.V(4).Info(newErr) + // return nil, newErr continue //TODO: Error here instead? } - klog.V(3).InfoS("BladesGetPortById success", "applId", applId, "bladeId", bladeId, "portId", port.GetId()) + klog.V(4).InfoS("success: BladesGetPortById", "applId", applId, "bladeId", bladeId, "portId", port.GetId()) ports = append(ports, port) } @@ -323,15 +351,24 @@ func (s *HostPortSummary) HostCount() int { // Find a specific Port by ID on a specific host func FindPortById_SingleHost(client *service.APIClient, hostId, portId string) (*service.PortInformation, error) { - requestPort := client.DefaultAPI.HostsGetPortById(context.Background(), hostId, portId) - port, response, err := requestPort.Execute() + request := client.DefaultAPI.HostsGetPortById(context.Background(), hostId, portId) + port, response, err := request.Execute() if err != nil { - msg := fmt.Sprintf("%T: Execute FAILURE", requestPort) - klog.ErrorS(err, msg, "response", response) - return nil, fmt.Errorf("failure: get host port by id: %s", err) + // Decode the JSON response into a struct + var status service.StatusMessage + if err := json.NewDecoder(response.Body).Decode(&status); err != nil { + newErr := fmt.Errorf("failure: Execute(%T): err(%s), error decoding response JSON", request, err) + klog.V(4).Info(newErr) + return nil, newErr + } + + newErr := fmt.Errorf("failure: Execute(%T): err(%s), uri(%s), details(%s), code(%d), message(%s)", + request, err, status.Uri, status.Details, status.Status.Code, status.Status.Message) + klog.V(4).Info(newErr) + return nil, newErr } - klog.V(3).InfoS("HostsGetPortById success", "hostId", hostId, "portId", port.GetId()) + klog.V(3).InfoS("success: HostsGetPortById", "hostId", hostId, "portId", port.GetId()) return port, nil } @@ -344,12 +381,21 @@ func FindPortById_AllHosts(client *service.APIClient, portId string) (*HostPortS requestHosts := client.DefaultAPI.HostsGet(context.Background()) hostsColl, response, err := requestHosts.Execute() if err != nil { - msg := fmt.Sprintf("%T: Execute FAILURE", requestHosts) - klog.ErrorS(err, msg, "response", response) - return nil, fmt.Errorf("failure: get hosts: %s", err) + // Decode the JSON response into a struct + var status service.StatusMessage + if err := json.NewDecoder(response.Body).Decode(&status); err != nil { + newErr := fmt.Errorf("failure: Execute(%T): err(%s), error decoding response JSON", requestHosts, err) + klog.V(4).Info(newErr) + return nil, newErr + } + + newErr := fmt.Errorf("failure: Execute(%T): err(%s), uri(%s), details(%s), code(%d), message(%s)", + requestHosts, err, status.Uri, status.Details, status.Status.Code, status.Status.Message) + klog.V(4).Info(newErr) + return nil, newErr } - klog.V(3).InfoS("HostsGet success", "hostsColl", hostsColl.GetMemberCount()) + klog.V(4).InfoS("success: HostsGet", "hostsColl", hostsColl.GetMemberCount()) if hostsColl.GetMemberCount() == 0 { klog.V(3).InfoS("FindPortById_AllHosts: no hosts found") @@ -378,12 +424,21 @@ func GetAllPorts_SingleHost(client *service.APIClient, hostId string) (*[]*servi requestPorts := client.DefaultAPI.HostsGetPorts(context.Background(), hostId) portsColl, response, err := requestPorts.Execute() if err != nil { - msg := fmt.Sprintf("%T: Execute FAILURE", requestPorts) - klog.ErrorS(err, msg, "response", response) - return nil, fmt.Errorf("failure: get host ports: %s", err) + // Decode the JSON response into a struct + var status service.StatusMessage + if err := json.NewDecoder(response.Body).Decode(&status); err != nil { + newErr := fmt.Errorf("failure: Execute(%T): err(%s), error decoding response JSON", requestPorts, err) + klog.V(4).Info(newErr) + return nil, newErr + } + + newErr := fmt.Errorf("failure: Execute(%T): err(%s), uri(%s), details(%s), code(%d), message(%s)", + requestPorts, err, status.Uri, status.Details, status.Status.Code, status.Status.Message) + klog.V(4).Info(newErr) + return nil, newErr } - klog.V(3).InfoS("PortsGet success", "hostId", hostId, "portsColl", portsColl.GetMemberCount()) + klog.V(4).InfoS("success: PortsGet", "hostId", hostId, "portsColl", portsColl.GetMemberCount()) for _, portMember := range portsColl.GetMembers() { portId := ReadLastItemFromUri(portMember.GetUri()) @@ -407,12 +462,21 @@ func GetAllPorts_AllHosts(client *service.APIClient) (*HostPortSummary, error) { requestHosts := client.DefaultAPI.HostsGet(context.Background()) hostColl, response, err := requestHosts.Execute() if err != nil { - msg := fmt.Sprintf("%T: Execute FAILURE", requestHosts) - klog.ErrorS(err, msg, "response", response) - return nil, fmt.Errorf("failure: get all hosts and ports: %s", err) + // Decode the JSON response into a struct + var status service.StatusMessage + if err := json.NewDecoder(response.Body).Decode(&status); err != nil { + newErr := fmt.Errorf("failure: Execute(%T): err(%s), error decoding response JSON", requestHosts, err) + klog.V(4).Info(newErr) + return nil, newErr + } + + newErr := fmt.Errorf("failure: Execute(%T): err(%s), uri(%s), details(%s), code(%d), message(%s)", + requestHosts, err, status.Uri, status.Details, status.Status.Code, status.Status.Message) + klog.V(4).Info(newErr) + return nil, newErr } - klog.V(3).InfoS("HostsGet success", "hostColl", hostColl.GetMemberCount()) + klog.V(4).InfoS("success: HostsGet", "hostColl", hostColl.GetMemberCount()) //Scan collection members for target host id for _, host := range hostColl.GetMembers() { diff --git a/cli/pkg/serviceLib/serviceWrap/resources.go b/cli/pkg/serviceLib/serviceWrap/resources.go index ef71b00..667a46b 100644 --- a/cli/pkg/serviceLib/serviceWrap/resources.go +++ b/cli/pkg/serviceLib/serviceWrap/resources.go @@ -4,6 +4,7 @@ package serviceWrap import ( "context" + "encoding/json" "fmt" service "cfm/pkg/client" @@ -53,15 +54,24 @@ func (s *ResourceBlockSummary) AddResourceSlice(applId, bladeId string, resource func FindResourceBlockOnBlade(client *service.APIClient, applId, bladeId, resourceId string) (*service.MemoryResourceBlock, error) { var resourceBlock *service.MemoryResourceBlock - requestResourceById := client.DefaultAPI.BladesGetResourceById(context.Background(), applId, bladeId, resourceId) - resourceBlock, response, err := requestResourceById.Execute() + request := client.DefaultAPI.BladesGetResourceById(context.Background(), applId, bladeId, resourceId) + resourceBlock, response, err := request.Execute() if err != nil { - msg := fmt.Sprintf("%T: Execute FAILURE", requestResourceById) - klog.ErrorS(err, msg, "response", response) - return nil, fmt.Errorf("failure: get appliance blade resource by id: %s", err) + // Decode the JSON response into a struct + var status service.StatusMessage + if err := json.NewDecoder(response.Body).Decode(&status); err != nil { + newErr := fmt.Errorf("failure: Execute(%T): err(%s), error decoding response JSON", request, err) + klog.V(4).Info(newErr) + return nil, newErr + } + + newErr := fmt.Errorf("failure: Execute(%T): err(%s), uri(%s), details(%s), code(%d), message(%s)", + request, err, status.Uri, status.Details, status.Status.Code, status.Status.Message) + klog.V(4).Info(newErr) + return nil, newErr } - klog.V(3).InfoS("BladesGetResourceById success", "applId", applId, "bladeId", bladeId, "resourceId", resourceBlock.GetId()) + klog.V(3).InfoS("FindResourceBlockOnBlade success", "applId", applId, "bladeId", bladeId, "resourceId", resourceBlock.GetId()) return resourceBlock, nil } @@ -72,26 +82,44 @@ func GetAllResourceBlocksForBlade(client *service.APIClient, applId, bladeId str requestResources := client.DefaultAPI.BladesGetResources(context.Background(), applId, bladeId) resourceColl, response, err := requestResources.Execute() if err != nil { - msg := fmt.Sprintf("%T: Execute FAILURE", requestResources) - klog.ErrorS(err, msg, "response", response) - // return nil, fmt.Errorf("failure: get appliance blade resources: %s", err) + // Decode the JSON response into a struct + var status service.StatusMessage + if err := json.NewDecoder(response.Body).Decode(&status); err != nil { + newErr := fmt.Errorf("failure: Execute(%T): err(%s), error decoding response JSON", requestResources, err) + klog.V(4).Info(newErr) + return nil, newErr + } + + newErr := fmt.Errorf("failure: Execute(%T): err(%s), uri(%s), details(%s), code(%d), message(%s)", + requestResources, err, status.Uri, status.Details, status.Status.Code, status.Status.Message) + klog.V(4).Info(newErr) + // return nil, newErr return &resources, nil //TODO: Error here instead? } - klog.V(3).InfoS("BladesGetResources success", "applId", applId, "bladeId", bladeId, "resourceColl", resourceColl.GetMemberCount()) + klog.V(4).InfoS("BladesGetResources success", "applId", applId, "bladeId", bladeId, "resourceColl", resourceColl.GetMemberCount()) for _, res := range resourceColl.GetMembers() { resourceId := ReadLastItemFromUri(res.GetUri()) requestResourceById := client.DefaultAPI.BladesGetResourceById(context.Background(), applId, bladeId, resourceId) resourceBlock, response, err := requestResourceById.Execute() if err != nil { - msg := fmt.Sprintf("%T: Execute FAILURE", requestResourceById) - klog.ErrorS(err, msg, "response", response) - // return nil, fmt.Errorf("failure: get appliance blade resource by id: %s", err) + // Decode the JSON response into a struct + var status service.StatusMessage + if err := json.NewDecoder(response.Body).Decode(&status); err != nil { + newErr := fmt.Errorf("failure: Execute(%T): err(%s), error decoding response JSON", requestResourceById, err) + klog.V(4).Info(newErr) + return nil, newErr + } + + newErr := fmt.Errorf("failure: Execute(%T): err(%s), uri(%s), details(%s), code(%d), message(%s)", + requestResourceById, err, status.Uri, status.Details, status.Status.Code, status.Status.Message) + klog.V(4).Info(newErr) + // return nil, newErr continue //TODO: Error here instead? } - klog.V(3).InfoS("BladesGetResourceById success", "applId", applId, "bladeId", bladeId, "resourceId", resourceBlock.GetId()) + klog.V(4).InfoS("BladesGetResourceById success", "applId", applId, "bladeId", bladeId, "resourceId", resourceBlock.GetId()) resources = append(resources, resourceBlock) } From 954397e423bdae8b59b11c06634cee9d6e05741e Mon Sep 17 00:00:00 2001 From: Mengling Ding <71745861+Meng-20@users.noreply.github.com> Date: Mon, 7 Oct 2024 11:35:56 -0600 Subject: [PATCH 08/12] Feat: display blade/host status in webui (#38) * feat: display blade status in cfm-webui * feat: display host status in cfm-webui * fix: data type mismatch * feat: change the device status color --- webui/src/components/Appliance/Appliances.vue | 61 ++++++++++-- webui/src/components/CXL-Hosts/CXL-Hosts.vue | 94 +++++++++++++++---- webui/src/components/CXL-Hosts/HostMemory.vue | 2 +- .../components/CXL-Hosts/HostMemoryDevice.vue | 2 +- webui/src/components/CXL-Hosts/HostPorts.vue | 2 +- webui/src/components/Stores/BladeStore.ts | 4 +- webui/src/components/Stores/HostStore.ts | 4 +- 7 files changed, 139 insertions(+), 30 deletions(-) diff --git a/webui/src/components/Appliance/Appliances.vue b/webui/src/components/Appliance/Appliances.vue index 6b0ddba..6ca6792 100644 --- a/webui/src/components/Appliance/Appliances.vue +++ b/webui/src/components/Appliance/Appliances.vue @@ -97,7 +97,8 @@ blade.ipAddress, blade.port, Number(blade.totalMemoryAvailableMiB), - Number(blade.totalMemoryAllocatedMiB) + Number(blade.totalMemoryAllocatedMiB), + blade.status ) " > @@ -168,6 +169,20 @@ service running on OpenBMC. + + Status + {{ selectedBladeStatus }} + + Appliance Id {{ selectedApplianceId }} + Blade Id {{ selectedBladeId }} + Ip Address {{ selectedBladeIp + ":" + selectedBladePort }} + @@ -1224,7 +1254,8 @@ export default { defaultBlade!.ipAddress, defaultBlade!.port, Number(defaultBlade!.totalMemoryAvailableMiB), - Number(defaultBlade!.totalMemoryAllocatedMiB) + Number(defaultBlade!.totalMemoryAllocatedMiB), + defaultBlade!.status ); } this.dialogAddBladeWait = false; @@ -1281,7 +1312,8 @@ export default { defaultBlade.ipAddress, defaultBlade.port, Number(defaultBlade.totalMemoryAvailableMiB), - Number(defaultBlade.totalMemoryAllocatedMiB) + Number(defaultBlade.totalMemoryAllocatedMiB), + defaultBlade.status ); } this.dialogDeleteBladeWait = false; @@ -1444,7 +1476,8 @@ export default { selectedBlade.ipAddress, selectedBlade.port, Number(selectedBlade.totalMemoryAvailableMiB), - Number(selectedBlade.totalMemoryAllocatedMiB) + Number(selectedBlade.totalMemoryAllocatedMiB), + selectedBlade.status ); // Update the URL with the first blade's ID updateUrlWithBladeId(newVal, selectedBlade.id); @@ -1496,6 +1529,17 @@ export default { const selectedBladeId = computed(() => bladeStore.selectedBladeId); const selectedBladeIp = computed(() => bladeStore.selectedBladeIp); const selectedBladePort = computed(() => bladeStore.selectedBladePortNum); + const selectedBladeStatus = computed(() => bladeStore.selectedBladeStatus); + + const statusColor = computed(() => { + return selectedBladeStatus.value === "online" ? "#6ebe4a" : "#ff9f40"; + }); + + const statusIcon = computed(() => { + return selectedBladeStatus.value === "online" + ? "mdi-check-circle" + : "mdi-close-circle"; + }); // Methods to update state const selectAppliance = (applianceId: string) => { @@ -1506,14 +1550,16 @@ export default { bladeIp: string, bladePort: number, bladeMemoryAvailable: number, - bladeMemoryAllocated: number + bladeMemoryAllocated: number, + bladeStatus: string | undefined ) => { bladeStore.selectBlade( bladeId, bladeIp, bladePort, bladeMemoryAvailable, - bladeMemoryAllocated + bladeMemoryAllocated, + bladeStatus ); }; @@ -1524,6 +1570,9 @@ export default { selectedBladeId, selectedBladePort, selectedBladeIp, + selectedBladeStatus, + statusColor, + statusIcon, selectAppliance, selectBlade, loading, diff --git a/webui/src/components/CXL-Hosts/CXL-Hosts.vue b/webui/src/components/CXL-Hosts/CXL-Hosts.vue index 59608a1..2dc64a8 100644 --- a/webui/src/components/CXL-Hosts/CXL-Hosts.vue +++ b/webui/src/components/CXL-Hosts/CXL-Hosts.vue @@ -31,7 +31,13 @@ :key="host.id" :id="host.id" @click=" - selectHost(host.id, host.ipAddress, host.port, host.localMemoryMiB) + selectHost( + host.id, + host.ipAddress, + host.port, + host.localMemoryMiB, + host.status + ) " > @@ -69,13 +75,9 @@ - + - + Basic Information + + Status + {{ selectedHostStatus }} + + CXL-Host Id {{ host.id }} + IpAddress {{ host.ipAddress + ":" + host.port }} + LocalMemoryGiB @@ -121,13 +148,18 @@ : "N/A" }} + - + - + Ports Information - - + + - + Memory Devices - + - + Memory hostStore.selectedHostId); const selectedHostIp = computed(() => hostStore.selectedHostIp); const selectedHostPort = computed(() => hostStore.selectedHostPortNum); + const selectedHostStatus = computed(() => hostStore.selectedHostStatus); + + const statusColor = computed(() => { + return selectedHostStatus.value === "online" ? "#6ebe4a" : "#ff9f40"; + }); + + const statusIcon = computed(() => { + return selectedHostStatus.value === "online" + ? "mdi-check-circle" + : "mdi-close-circle"; + }); // Methods to update state const selectHost = ( hostId: string, hostIp: string, hostPort: number, - hostLocalMemory: number | undefined + hostLocalMemory: number | undefined, + hostStatus: string | undefined ) => { - hostStore.selectHost(hostId, hostIp, hostPort, hostLocalMemory); + hostStore.selectHost( + hostId, + hostIp, + hostPort, + hostLocalMemory, + hostStatus + ); }; return { @@ -897,6 +950,9 @@ export default { selectedHostId, selectedHostPort, selectedHostIp, + selectedHostStatus, + statusColor, + statusIcon, selectHost, loading, }; diff --git a/webui/src/components/CXL-Hosts/HostMemory.vue b/webui/src/components/CXL-Hosts/HostMemory.vue index 5611a84..1764378 100644 --- a/webui/src/components/CXL-Hosts/HostMemory.vue +++ b/webui/src/components/CXL-Hosts/HostMemory.vue @@ -4,7 +4,7 @@ diff --git a/webui/src/components/CXL-Hosts/HostMemoryDevice.vue b/webui/src/components/CXL-Hosts/HostMemoryDevice.vue index 55f9a7f..5f25ce6 100644 --- a/webui/src/components/CXL-Hosts/HostMemoryDevice.vue +++ b/webui/src/components/CXL-Hosts/HostMemoryDevice.vue @@ -4,7 +4,7 @@ diff --git a/webui/src/components/CXL-Hosts/HostPorts.vue b/webui/src/components/CXL-Hosts/HostPorts.vue index 28d6afc..2b9602c 100644 --- a/webui/src/components/CXL-Hosts/HostPorts.vue +++ b/webui/src/components/CXL-Hosts/HostPorts.vue @@ -4,7 +4,7 @@ diff --git a/webui/src/components/Stores/BladeStore.ts b/webui/src/components/Stores/BladeStore.ts index b93761c..bddaee2 100644 --- a/webui/src/components/Stores/BladeStore.ts +++ b/webui/src/components/Stores/BladeStore.ts @@ -14,6 +14,7 @@ export const useBladeStore = defineStore('blade', { selectedBladePortNum: null as unknown as number, selectedBladeTotalMemoryAvailableMiB: null as unknown as number | undefined, selectedBladeTotalMemoryAllocatedMiB: null as unknown as number | undefined, + selectedBladeStatus: null as unknown as string | undefined, addBladeError: null as unknown, deleteBladeError: null as unknown, resyncBladeError: null as unknown, @@ -145,12 +146,13 @@ export const useBladeStore = defineStore('blade', { }, - selectBlade(bladeId: string, selectedBladeIp: string, selectBladePortNum: number, selectedBladeTotalMemoryAvailableMiB: number, selectedBladeTotalMemoryAllocatedMiB: number) { + selectBlade(bladeId: string, selectedBladeIp: string, selectBladePortNum: number, selectedBladeTotalMemoryAvailableMiB: number, selectedBladeTotalMemoryAllocatedMiB: number, status: string | undefined) { this.selectedBladeId = bladeId; this.selectedBladeIp = selectedBladeIp; this.selectedBladePortNum = selectBladePortNum; this.selectedBladeTotalMemoryAvailableMiB = selectedBladeTotalMemoryAvailableMiB; this.selectedBladeTotalMemoryAllocatedMiB = selectedBladeTotalMemoryAllocatedMiB; + this.selectedBladeStatus = status; }, updateSelectedBladeMemory(availableMemory: number | undefined, allocatedMemory: number | undefined) { diff --git a/webui/src/components/Stores/HostStore.ts b/webui/src/components/Stores/HostStore.ts index 3c04cae..b90e8f4 100644 --- a/webui/src/components/Stores/HostStore.ts +++ b/webui/src/components/Stores/HostStore.ts @@ -14,6 +14,7 @@ export const useHostStore = defineStore('host', { selectedHostIp: null as unknown as string, selectedHostPortNum: null as unknown as number, selectedHostLocalMemory: null as unknown as number | undefined, + selectedHostStatus: null as unknown as string | undefined, addHostError: null as unknown, deleteHostError: null as unknown, resyncHostError: null as unknown, @@ -123,11 +124,12 @@ export const useHostStore = defineStore('host', { } }, - selectHost(selectedHostId: string, selectedHostIp: string, selectedHostPortNum: number, selectedHostLocalMemory: number | undefined) { + selectHost(selectedHostId: string, selectedHostIp: string, selectedHostPortNum: number, selectedHostLocalMemory: number | undefined, status: string | undefined) { this.selectedHostId = selectedHostId; this.selectedHostIp = selectedHostIp; this.selectedHostPortNum = selectedHostPortNum; this.selectedHostLocalMemory = selectedHostLocalMemory; + this.selectedHostStatus = status }, } }) \ No newline at end of file From 06dbdf1ff82040798e6b0aa2f80562ee1b5c0fc8 Mon Sep 17 00:00:00 2001 From: Mengling Ding <71745861+Meng-20@users.noreply.github.com> Date: Mon, 7 Oct 2024 17:14:15 -0600 Subject: [PATCH 09/12] feat: rename appliance/blade/host * feat: add api appliancesRenameById * feat: add api bladesRenameById * feat: add api RenameHostById * feat: change to use PUT method * feat: error handling * feat: change the API name from rename to update * refactor: remove unnecessary code --- api/cfm-openapi.yaml | 107 +++++++++++++++++++++++++++++++++ pkg/api/api_default_service.go | 90 +++++++++++++++++++++++++++ pkg/common/status.go | 15 ++++- pkg/manager/appliance.go | 21 ++++++- pkg/manager/manager.go | 107 +++++++++++++++++++++++++++++++++ 5 files changed, 338 insertions(+), 2 deletions(-) diff --git a/api/cfm-openapi.yaml b/api/cfm-openapi.yaml index 51ee97f..09d772f 100644 --- a/api/cfm-openapi.yaml +++ b/api/cfm-openapi.yaml @@ -187,6 +187,41 @@ paths: application/json: schema: $ref: "#/components/schemas/statusMessage" + put: + description: Update an appliance with a new id. + operationId: appliancesUpdateById + parameters: + - $ref: "#/components/parameters/applianceId" + - name: newApplianceId + in: query + required: true + schema: + type: string + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/appliance' + "400": + description: Bad Request + content: + application/json: + schema: + $ref: '#/components/schemas/statusMessage' + "404": + description: Not Found + content: + application/json: + schema: + $ref: '#/components/schemas/statusMessage' + "500": + description: Internal Server Error + content: + application/json: + schema: + $ref: '#/components/schemas/statusMessage' # Memory Appliance Blades /cfm/v1/appliances/{applianceId}/blades: @@ -328,6 +363,42 @@ paths: application/json: schema: $ref: "#/components/schemas/statusMessage" + put: + description: Update a blade with a new id. + operationId: bladesUpdateById + parameters: + - $ref: "#/components/parameters/applianceId" + - $ref: "#/components/parameters/bladeId" + - name: newBladeId + in: query + required: true + schema: + type: string + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/blade' + "400": + description: Bad Request + content: + application/json: + schema: + $ref: '#/components/schemas/statusMessage' + "404": + description: Not Found + content: + application/json: + schema: + $ref: '#/components/schemas/statusMessage' + "500": + description: Internal Server Error + content: + application/json: + schema: + $ref: "#/components/schemas/statusMessage" # Memory Blade Memory Resource Blocks /cfm/v1/appliances/{applianceId}/blades/{bladeId}/resources: @@ -808,6 +879,42 @@ paths: application/json: schema: $ref: "#/components/schemas/statusMessage" + put: + description: Update a CXL host with a new id. + operationId: hostsUpdateById + parameters: + - $ref: "#/components/parameters/hostId" + - name: newHostId + in: query + required: true + schema: + type: string + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/host' + "400": + description: Bad Request + content: + application/json: + schema: + $ref: '#/components/schemas/statusMessage' + "404": + description: Not Found + content: + application/json: + schema: + $ref: '#/components/schemas/statusMessage' + "500": + description: Internal Server Error + content: + application/json: + schema: + $ref: "#/components/schemas/statusMessage" + # CXL Host Ports /cfm/v1/hosts/{hostId}/ports: get: diff --git a/pkg/api/api_default_service.go b/pkg/api/api_default_service.go index e58031f..0019460 100644 --- a/pkg/api/api_default_service.go +++ b/pkg/api/api_default_service.go @@ -143,6 +143,33 @@ func (cfm *CfmApiService) AppliancesPost(ctx context.Context, credentials openap return openapi.Response(http.StatusCreated, a), nil } +// AppliancesUpdateById - +func (cfm *CfmApiService) AppliancesUpdateById(ctx context.Context, applianceId string, newApplianceId string) (openapi.ImplResponse, error) { + // Make sure the newApplianceId doesn't exist + _, exist := manager.GetApplianceById(ctx, newApplianceId) + if exist == nil { + err := common.RequestError{ + StatusCode: common.StatusApplianceIdDuplicate, + Err: fmt.Errorf("the new name (%s) already exists", newApplianceId), + } + return formatErrorResp(ctx, &err) + } + + // Make sure the appliance exists + appliance, err := manager.GetApplianceById(ctx, applianceId) + if err != nil { + return formatErrorResp(ctx, err.(*common.RequestError)) + } + + //Rename the appliance with the new id + newAppliance, err := manager.RenameAppliance(ctx, appliance, newApplianceId) + if err != nil { + return formatErrorResp(ctx, err.(*common.RequestError)) + } + + return openapi.Response(http.StatusOK, newAppliance), nil +} + // AppliancesResync - func (cfm *CfmApiService) AppliancesResyncById(ctx context.Context, applianceId string) (openapi.ImplResponse, error) { appliance, err := manager.ResyncApplianceById(ctx, applianceId) @@ -495,6 +522,41 @@ func (cfm *CfmApiService) BladesGetPorts(ctx context.Context, applianceId string return openapi.Response(http.StatusOK, response), nil } +// BladesUpdateById - +func (cfm *CfmApiService) BladesUpdateById(ctx context.Context, applianceId string, bladeId string, newBladeId string) (openapi.ImplResponse, error) { + appliance, err := manager.GetApplianceById(ctx, applianceId) + if err != nil { + return formatErrorResp(ctx, err.(*common.RequestError)) + } + + // Make sure the bladeId exists + // Get the blade information from the manager level and is used for renaming + blade, err := appliance.GetBladeById(ctx, bladeId) + if err != nil { + return formatErrorResp(ctx, err.(*common.RequestError)) + } + + // Make sure the newBladeId doesn't exist + existBladeIds := appliance.GetAllBladeIds() + for _, id := range existBladeIds { + if newBladeId == id { + err := common.RequestError{ + StatusCode: common.StatusBladeIdDuplicate, + Err: fmt.Errorf("the new name (%s) already exists", newBladeId), + } + return formatErrorResp(ctx, &err) + } + } + + //Rename the appliance with the new id + newBlade, err := manager.RenameBlade(ctx, appliance, blade, newBladeId) + if err != nil { + return formatErrorResp(ctx, err.(*common.RequestError)) + } + + return openapi.Response(http.StatusOK, newBlade), nil +} + // BladesGetResourceById - func (cfm *CfmApiService) BladesGetResourceById(ctx context.Context, applianceId string, bladeId string, resourceId string) (openapi.ImplResponse, error) { appliance, err := manager.GetApplianceById(ctx, applianceId) @@ -932,6 +994,34 @@ func (cfm *CfmApiService) HostsPost(ctx context.Context, credentials openapi.Cre return openapi.Response(http.StatusCreated, h), nil } +// HostsUpdateById - +func (cfm *CfmApiService) HostsUpdateById(ctx context.Context, hostId string, newHostId string) (openapi.ImplResponse, error) { + // Make sure the hostId exists + // Get the host information from the manager level and is used for renaming + host, err := manager.GetHostById(ctx, hostId) + if err != nil { + return formatErrorResp(ctx, err.(*common.RequestError)) + } + + // Make sure the newHostId doesn't exist + _, exist := manager.GetHostById(ctx, newHostId) + if exist == nil { + err := common.RequestError{ + StatusCode: common.StatusHostIdDuplicate, + Err: fmt.Errorf("the new name (%s) already exists", newHostId), + } + return formatErrorResp(ctx, &err) + } + + //Rename the cxl host with the new id + newHost, err := manager.RenameHost(ctx, host, newHostId) + if err != nil { + return formatErrorResp(ctx, err.(*common.RequestError)) + } + + return openapi.Response(http.StatusOK, newHost), nil +} + // HostsResync - func (cfm *CfmApiService) HostsResyncById(ctx context.Context, hostId string) (openapi.ImplResponse, error) { host, err := manager.ResyncHostById(ctx, hostId) diff --git a/pkg/common/status.go b/pkg/common/status.go index 259308c..3f067e6 100644 --- a/pkg/common/status.go +++ b/pkg/common/status.go @@ -60,6 +60,10 @@ const ( StatusBladeResyncFailure //409 StatusHostResyncFailure //409 + StatusApplianceRenameFailure //409 + StatusBladeRenameFailure //409 + StatusHostRenameFailure //409 + StatusApplianceIdDuplicate //409 StatusBladeIdDuplicate //409 StatusMemoryIdDuplicate //409 @@ -176,6 +180,12 @@ func (e StatusCodeType) String() string { return "Port Id Already Exist" case StatusHostIdDuplicate: return "Host Id Already Exist" + case StatusApplianceRenameFailure: + return "Rename Appliance Failure" + case StatusBladeRenameFailure: + return "Rename Blade Failure" + case StatusHostRenameFailure: + return "Rename Host Failure" } return "Unknown" @@ -220,7 +230,10 @@ func (e StatusCodeType) HttpStatusCode() int { StatusApplianceIdDuplicate, StatusBladeIdDuplicate, StatusPortIdDuplicate, - StatusHostIdDuplicate: + StatusHostIdDuplicate, + StatusApplianceRenameFailure, + StatusBladeRenameFailure, + StatusHostRenameFailure: return http.StatusConflict // 409 case StatusBackendInterfaceFailure, StatusBladeCreateSessionFailure, diff --git a/pkg/manager/appliance.go b/pkg/manager/appliance.go index af5f991..6ac2498 100644 --- a/pkg/manager/appliance.go +++ b/pkg/manager/appliance.go @@ -296,6 +296,25 @@ func (a *Appliance) InvalidateCache() { } } +func (a *Appliance) AddBladeBack(ctx context.Context, c *openapi.Credentials) (*Blade, error) { + logger := klog.FromContext(ctx) + logger.V(4).Info(">>>>>> Add Blade Back: ", "bladeId", c.CustomId, "applianceId", a.Id) + + // add blade back + blade, err := a.AddBlade(ctx, c) + if err != nil { + newErr := fmt.Errorf("failed to add blade [%s] back", c.CustomId) + logger.Error(newErr, "failure: add blade back") + return nil, &common.RequestError{StatusCode: common.StatusBladeCreateSessionFailure, Err: newErr} + } + + blade.UpdateConnectionStatusBackend(ctx) + + logger.V(2).Info("success: add blade back", "status", blade.Status, "bladeId", blade.Id, "applianceId", a.Id) + + return blade, nil +} + func (a *Appliance) ResyncBladeById(ctx context.Context, bladeId string) (*Blade, error) { logger := klog.FromContext(ctx) logger.V(4).Info(">>>>>> ResyncBladeById: ", "bladeId", bladeId, "applianceId", a.Id) @@ -305,7 +324,7 @@ func (a *Appliance) ResyncBladeById(ctx context.Context, bladeId string) (*Blade if !ok || blade == nil { newErr := fmt.Errorf("failed to get blade [%s]", bladeId) logger.Error(newErr, "failure: resync blade by id") - return nil, &common.RequestError{StatusCode: common.StatusHostIdDoesNotExist, Err: newErr} + return nil, &common.RequestError{StatusCode: common.StatusBladeIdDoesNotExist, Err: newErr} } blade.UpdateConnectionStatusBackend(ctx) diff --git a/pkg/manager/manager.go b/pkg/manager/manager.go index fc20617..a349f6f 100644 --- a/pkg/manager/manager.go +++ b/pkg/manager/manager.go @@ -119,6 +119,85 @@ func GetAppliances(ctx context.Context) map[string]*Appliance { return appliances } +func RenameAppliance(ctx context.Context, appliance *Appliance, newApplianceId string) (*Appliance, error) { + logger := klog.FromContext(ctx) + logger.V(4).Info(">>>>>> RenameApplianceById: ", "applianceId", appliance.Id) + + // Store the associated blades information locally, which is needed when adding back the blades + bladesInfo := make(map[string]*Blade) + for _, id := range appliance.GetAllBladeIds() { + bladesInfo[id] = appliance.Blades[id] + } + + // delete appliance and the associated blades + _, err := DeleteApplianceById(ctx, appliance.Id) + if err != nil { + return nil, &common.RequestError{StatusCode: common.StatusApplianceDeleteSessionFailure, Err: err} + } + + // add appliance back with the new id + c := openapi.Credentials{ + CustomId: newApplianceId, + } + newAppliance, err := AddAppliance(ctx, &c) + if err != nil { + return nil, &common.RequestError{StatusCode: common.StatusApplianceCreateSessionFailure, Err: err} + } + + var failedBladeIds []string + + // Add blades back to the new appliance + for id, blade := range bladesInfo { + _, err := newAppliance.AddBladeBack(ctx, blade.creds) + if err != nil { + newErr := fmt.Errorf("add blade by id [%s] failure: appliance [%s]: %w", id, newApplianceId, err) + logger.Error(newErr, "failure: add blade to new appliance: handle and continue") + failedBladeIds = append(failedBladeIds, id) + } + } + + if len(failedBladeIds) == 0 { + logger.V(2).Info("success: rename appliance", "applianceId", newApplianceId, "blades", bladesInfo) + return newAppliance, nil + } else if len(failedBladeIds) < len(bladesInfo) { + newErr := fmt.Errorf("rename appliance by id [%s]: some failure(s): blade(s) [%s]", newApplianceId, failedBladeIds) + logger.Error(newErr, "partial success: rename appliance by id") + return newAppliance, &common.RequestError{StatusCode: common.StatusApplianceRenameFailure, Err: newErr} + } else { + newErr := fmt.Errorf("rename appliance by id [%s] failure", newApplianceId) + logger.Error(newErr, "failure: rename appliance by id") + return nil, &common.RequestError{StatusCode: common.StatusApplianceRenameFailure, Err: newErr} + } +} + +func RenameBlade(ctx context.Context, appliance *Appliance, blade *Blade, newBladeId string) (*Blade, error) { + logger := klog.FromContext(ctx) + logger.V(4).Info(">>>>>> RenameBladeById: ", "bladeId", blade.Id) + + // Save the blade credentials for adding back with the new name + c := &openapi.Credentials{ + Username: blade.creds.Username, + Password: blade.creds.Password, + IpAddress: blade.creds.IpAddress, + Port: blade.creds.Port, + Insecure: blade.creds.Insecure, + Protocol: blade.creds.Protocol, + CustomId: newBladeId, + } + + // delete blade + _, err := appliance.DeleteBladeById(ctx, blade.Id) + if err != nil { + return nil, &common.RequestError{StatusCode: common.StatusBladeRenameFailure, Err: err} + } + // Add the balde back with the new name + newBlade, err := appliance.AddBlade(ctx, c) + if err != nil { + return nil, &common.RequestError{StatusCode: common.StatusBladeRenameFailure, Err: err} + } + return newBlade, nil +} + func ResyncApplianceById(ctx context.Context, applianceId string) (*Appliance, error) { logger := klog.FromContext(ctx) logger.V(4).Info(">>>>>> ResyncApplianceById: ", "applianceId", applianceId) @@ -267,6 +346,34 @@ func AddHost(ctx context.Context, c *openapi.Credentials) (*Host, error) { return host, nil } +func RenameHost(ctx context.Context, host *Host, newHostId string) (*Host, error) { + logger := klog.FromContext(ctx) + logger.V(4).Info(">>>>>> RenameHostById: ", "hostId", host.Id) + // Save the host credentials for adding back with the new name + c := &openapi.Credentials{ + Username: host.creds.Username, + Password: host.creds.Password, + IpAddress: host.creds.IpAddress, + Port: host.creds.Port, + Insecure: host.creds.Insecure, + Protocol: host.creds.Protocol, + CustomId: newHostId, + } + + // delete host + _, err := DeleteHostById(ctx, host.Id) + if err != nil { + return nil, &common.RequestError{StatusCode: common.StatusBladeRenameFailure, Err: err} + } + + // Add the host back with the new name + newHost, err := AddHost(ctx, c) + if err != nil { + return nil, &common.RequestError{StatusCode: common.StatusBladeRenameFailure, Err: err} + } + return newHost, nil +} + func DeleteHostById(ctx context.Context, hostId string) (*Host, error) { logger := klog.FromContext(ctx) logger.V(4).Info(">>>>>> DeleteHostById: ", "hostId", hostId) From 88fe381e43fed543fd8e9370e7f050adf190d90a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Oct 2024 17:28:21 -0600 Subject: [PATCH 10/12] build(deps): bump rollup from 3.29.4 to 3.29.5 in /webui Bumps [rollup](https://github.com/rollup/rollup) from 3.29.4 to 3.29.5. - [Release notes](https://github.com/rollup/rollup/releases) - [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md) - [Commits](https://github.com/rollup/rollup/compare/v3.29.4...v3.29.5) --- updated-dependencies: - dependency-name: rollup dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- webui/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/webui/package-lock.json b/webui/package-lock.json index d00f3bc..df83d14 100644 --- a/webui/package-lock.json +++ b/webui/package-lock.json @@ -2899,9 +2899,9 @@ "integrity": "sha512-OlwfYEgA2RdboZohpldlvJ1xngOins5d7ejqnIBWr9KaMxsnBqotpptRXTyfNRLnFpqzX6sTDt+X+a+6udnU8g==" }, "node_modules/rollup": { - "version": "3.29.4", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz", - "integrity": "sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==", + "version": "3.29.5", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.5.tgz", + "integrity": "sha512-GVsDdsbJzzy4S/v3dqWPJ7EfvZJfCHiDqe80IyrF59LYuP+e6U1LJoUqeuqRbwAWoMNoXivMNeNAOf5E22VA1w==", "devOptional": true, "bin": { "rollup": "dist/bin/rollup" From 9b78d7ddfa52ed4ea4ae26a2b24dc7ef196a3d9e Mon Sep 17 00:00:00 2001 From: Mengling Ding <71745861+Meng-20@users.noreply.github.com> Date: Tue, 15 Oct 2024 11:21:17 -0600 Subject: [PATCH 11/12] feat/rename device in webui (#41) * fix: fix the return mismatch onr appliance rename in cfm-service * feat: rename appliance by id * fix: fix the return mismatch on blade rename in cfm-service * feat: rename blade by id * feat: remove blade resync button in the Basic Information component * fix: fix the return mismatch on host rename in cfm-service * feat: rename host by id and remove resync button in the basic information component * fix: fix the data type mismatch problem --- pkg/api/api_default_service.go | 64 +- webui/src/components/Appliance/Appliances.vue | 548 ++++++++++++++++-- webui/src/components/CXL-Hosts/CXL-Hosts.vue | 298 ++++++++-- webui/src/components/Stores/ApplianceStore.ts | 32 + webui/src/components/Stores/BladeStore.ts | 32 + webui/src/components/Stores/HostStore.ts | 32 + 6 files changed, 932 insertions(+), 74 deletions(-) diff --git a/pkg/api/api_default_service.go b/pkg/api/api_default_service.go index 0019460..e295843 100644 --- a/pkg/api/api_default_service.go +++ b/pkg/api/api_default_service.go @@ -167,7 +167,19 @@ func (cfm *CfmApiService) AppliancesUpdateById(ctx context.Context, applianceId return formatErrorResp(ctx, err.(*common.RequestError)) } - return openapi.Response(http.StatusOK, newAppliance), nil + a := openapi.Appliance{ + Id: newAppliance.Id, + IpAddress: "", // Unused + Port: 0, // Unused + Status: "", // Unused + Blades: openapi.MemberItem{ + Uri: manager.GetCfmUriBlades(newAppliance.Id), + }, + TotalMemoryAvailableMiB: 0, + TotalMemoryAllocatedMiB: 0, + } + + return openapi.Response(http.StatusOK, a), nil } // AppliancesResync - @@ -554,7 +566,30 @@ func (cfm *CfmApiService) BladesUpdateById(ctx context.Context, applianceId stri return formatErrorResp(ctx, err.(*common.RequestError)) } - return openapi.Response(http.StatusOK, newBlade), nil + totals, err := newBlade.GetResourceTotals(ctx) + if err != nil { + return formatErrorResp(ctx, err.(*common.RequestError)) + } + + b := openapi.Blade{ + Id: newBlade.Id, + IpAddress: newBlade.GetNetIp(), + Port: int32(newBlade.GetNetPort()), + Status: string(newBlade.Status), + Ports: openapi.MemberItem{ + Uri: manager.GetCfmUriBladePorts(appliance.Id, newBlade.Id), + }, + Resources: openapi.MemberItem{ + Uri: manager.GetCfmUriBladeResources(appliance.Id, newBlade.Id), + }, + Memory: openapi.MemberItem{ + Uri: manager.GetCfmUriBladeMemory(appliance.Id, newBlade.Id), + }, + TotalMemoryAvailableMiB: totals.TotalMemoryAvailableMiB, + TotalMemoryAllocatedMiB: totals.TotalMemoryAllocatedMiB, + } + + return openapi.Response(http.StatusOK, b), nil } // BladesGetResourceById - @@ -1019,7 +1054,30 @@ func (cfm *CfmApiService) HostsUpdateById(ctx context.Context, hostId string, ne return formatErrorResp(ctx, err.(*common.RequestError)) } - return openapi.Response(http.StatusOK, newHost), nil + totals, err := newHost.GetMemoryTotals(ctx) + if err != nil { + return formatErrorResp(ctx, err.(*common.RequestError)) + } + + h := openapi.Host{ + Id: newHost.Id, + IpAddress: newHost.GetNetIp(), + Port: int32(newHost.GetNetPort()), + Status: string(newHost.Status), + Ports: openapi.MemberItem{ + Uri: manager.GetCfmUriHostPorts(newHost.Id), + }, + Memory: openapi.MemberItem{ + Uri: manager.GetCfmUriHostMemory(newHost.Id), + }, + MemoryDevices: openapi.MemberItem{ + Uri: manager.GetCfmUriHostMemoryDevices(newHost.Id), + }, + LocalMemoryMiB: totals.LocalMemoryMib, + RemoteMemoryMiB: totals.RemoteMemoryMib, + } + + return openapi.Response(http.StatusOK, h), nil } // HostsResync - diff --git a/webui/src/components/Appliance/Appliances.vue b/webui/src/components/Appliance/Appliances.vue index 6ca6792..5565d52 100644 --- a/webui/src/components/Appliance/Appliances.vue +++ b/webui/src/components/Appliance/Appliances.vue @@ -45,18 +45,34 @@ {{ appliance.id }} - - mdi-close - Click here to delete this memory appliance - + + + + + + {{ item.text }} + + + @@ -105,18 +121,43 @@ {{ blade.id }} - - mdi-close - Click here to delete this blade - + + + + + + {{ item.text }} + + + @@ -152,19 +193,7 @@ > -

- Blade - - mdi-sync-circle - Click here to resynchronize this blade - -

+

Blade

A blade is associated with one appliance and it is Redfish service running on OpenBMC.
@@ -592,6 +621,295 @@
+ + + + + Rename Appliance + + + + + + +
+ + +
+
+
+
+
+ + + + + Cancel + + + Rename + + +
+
+ + + + +

Rename an appliance succeeded!

+

+ New Appliance Id: +
{{ renamedApplianceId }} +

+ +
+ + Done + +
+
+
+ + + + +

Rename an appliance failed!

+

+ {{ renameApplianceError }} +

+ +
+ + Done + +
+
+
+ + + + + + + + + + + + + + + + Rename Blade + + + + + + +
+ + +
+
+
+
+
+ + + + + Cancel + + + Rename + + +
+
+ + + + +

Rename a Blade succeeded!

+

+ New Blade Id: +
{{ renamedBladeId }} +

+ +
+ + Done + +
+
+
+ + + + +

Rename a Blade failed!

+

+ {{ renameBladeError }} +

+ +
+ + Done + +
+
+
+ + + + + + + + + + + applianceStore.appliances); + if (appliances.value.length > 0) { + applianceStore.selectAppliance(newApplianceId); + } + + this.dialogRenameApplianceWait = false; + this.dialogRenameApplianceSuccess = true; + } else { + this.dialogRenameApplianceWait = false; + this.dialogRenameApplianceFailure = true; + } + + // Reset the credentials + this.renameApplianceCredentials = { + customId: "", + }; + }, + + renameBlade() { + this.dialogRenameBlade = true; + }, + + /* Triggle the API bladesUpdateById in blade store to rename a blade */ + async renameBladeConfirm( + applianceId: string, + bladeId: string, + newBladeId: string + ) { + // Make the rename blade popup disappear and waiting popup appear + this.dialogRenameBlade = false; + this.dialogRenameBladeWait = true; + + const bladeStore = useBladeStore(); + const newBlade = await bladeStore.renameBlade( + applianceId, + bladeId, + newBladeId + ); + + this.renameBladeError = bladeStore.renameBladeError as string; + + if (!this.renameBladeError) { + this.renamedBladeId = newBlade?.id; + + // Set the renamed Blade as the selected Blade + const Blades = computed(() => bladeStore.blades); + if (Blades.value.length > 0) { + const defaultBlade = newBlade; + bladeStore.selectBlade( + defaultBlade!.id, + defaultBlade!.ipAddress, + defaultBlade!.port, + Number(defaultBlade!.totalMemoryAvailableMiB), + Number(defaultBlade!.totalMemoryAllocatedMiB), + defaultBlade!.status + ); + } + + this.dialogRenameBladeWait = false; + this.dialogRenameBladeSuccess = true; + } else { + this.dialogRenameBladeWait = false; + this.dialogRenameBladeFailure = true; + } + + // Reset the credentials + this.renameBladeCredentials = { + customId: "", + }; + }, + /* Open the add blade popup */ addNewBladeWindowButton() { this.dialogNewBlade = true; diff --git a/webui/src/components/CXL-Hosts/CXL-Hosts.vue b/webui/src/components/CXL-Hosts/CXL-Hosts.vue index 2dc64a8..2f70f49 100644 --- a/webui/src/components/CXL-Hosts/CXL-Hosts.vue +++ b/webui/src/components/CXL-Hosts/CXL-Hosts.vue @@ -25,6 +25,14 @@ color="#6ebe4a" bg-color="rgba(110, 190, 74, 0.1)" > + + + mdi-plus-thick + Click here to add new cxl-host + + {{ host.id }} - - mdi-close - Click here to delete this cxl-host - + + + + + + {{ item.text }} + + +
- - - mdi-plus-thick - Click here to add new cxl-host - - @@ -84,20 +108,7 @@ >
-

- CXL-Host - - mdi-sync-circle - Click here to resynchronize this CXL-Host - device - -

+

CXL-Host

๐Ÿ’ปA CXL-Host device is a Redfish Service agent providing local memory composition.
@@ -626,6 +637,147 @@
+ + + + + + Rename Host + + + + + + +
+ + +
+
+
+
+
+ + + + + Cancel + + + Rename + + +
+
+ + + + +

Rename a Host succeeded!

+

+ New Host Id: +
{{ renamedHostId }} +

+ +
+ + Done + +
+
+
+ + + + +

Rename a Host failed!

+

+ {{ renameHostError }} +

+ +
+ + Done + +
+
+
+ + + + + + + + + + @@ -645,6 +797,8 @@ export default { return { loadProgressText: "Loading the page, please wait...", resyncHostProgressText: "Resynchronizing the CXL-Host, please wait...", + renameHostProgressText: "Renaming the CXL-Host, please wait...", + // The rules for the input fields when adding a new cxl-host rules: { required: (value: any) => !!value || "Field is required", @@ -682,6 +836,40 @@ export default { dialogResyncHostSuccess: false, dialogResyncHostFailure: false, resyncHostError: null as unknown, + + renameHostCredentials: { + customId: "", + }, + renamedHostId: null as unknown as string | undefined, // Be used on success popup + dialogRenameHost: false, + renameHostError: null as unknown, + dialogRenameHostSuccess: false, + dialogRenameHostFailure: false, + dialogRenameHostWait: false, + + hostDropItems: [ + { + text: "Delete", + icon: "mdi-delete", + function: this.deleteHostWindowButton, + id: "deleteHostWindow", + iconColor: "warning", + }, + { + text: "Rename", + icon: "mdi-rename-box", + function: this.renameHost, + id: "renameHostWindow", + iconColor: "primary", + }, + { + text: "Resync", + icon: "mdi-sync-circle", + function: this.resyncHostWindowButton, + id: "resyncHostWindow", + iconColor: "#6ebe4a", + }, + ], }; }, @@ -803,6 +991,48 @@ export default { } }, + renameHost() { + this.dialogRenameHost = true; + }, + + /* Triggle the API hostsUpdateById in host store to rename a host */ + async renameHostConfirm(hostId: string, newHostId: string) { + // Make the rename host popup disappear and waiting popup appear + this.dialogRenameHost = false; + this.dialogRenameHostWait = true; + + const hostStore = useHostStore(); + const newHost = await hostStore.renameHost(hostId, newHostId); + this.renameHostError = hostStore.renameHostError as string; + + if (!this.renameHostError) { + this.renamedHostId = newHost?.id; + + // Set the renamed host as the selected host + const Hosts = computed(() => hostStore.hosts); + if (Hosts.value.length > 0) { + hostStore.selectHost( + newHost?.id + "", + newHost?.ipAddress + "", + Number(newHost?.port), + newHost?.localMemoryMiB, + newHost?.status + ); + } + + this.dialogRenameHostWait = false; + this.dialogRenameHostSuccess = true; + } else { + this.dialogRenameHostWait = false; + this.dialogRenameHostFailure = true; + } + + // Reset the credentials + this.renameHostCredentials = { + customId: "", + }; + }, + // Method to manually update the content for the resync cxl-host async updateHostContent(hostId: string) { const hostPortStore = useHostPortStore(); diff --git a/webui/src/components/Stores/ApplianceStore.ts b/webui/src/components/Stores/ApplianceStore.ts index 530647e..661f777 100644 --- a/webui/src/components/Stores/ApplianceStore.ts +++ b/webui/src/components/Stores/ApplianceStore.ts @@ -13,10 +13,41 @@ export const useApplianceStore = defineStore('appliance', { selectedApplianceId: null as unknown as string, addApplianceError: null as unknown, deleteApplianceError: null as unknown, + renameApplianceError: null as unknown, applianceIds: [] as { id: string, bladeIds: string[] }[], }), actions: { + async renameAppliance(applianceId: string, newApplianceId: string) { + this.renameApplianceError = ""; + try { + const defaultApi = new DefaultApi(undefined, API_BASE_PATH); + const response = await defaultApi.appliancesUpdateById(applianceId, newApplianceId); + + // Update the appliances array + if (response) { + this.appliances = this.appliances.filter( + (appliance) => appliance.id !== applianceId + ); + this.appliances.push(response.data); + } + + return response.data + } catch (error) { + if (axios.isAxiosError(error)) { + this.renameApplianceError = error.message; + + if (error.response) { + this.renameApplianceError = error.response?.data.status.message + " (" + error.response?.request.status + ")"; + } + } + else { + this.renameApplianceError = error; + } + console.error("Error:", error); + } + }, + async fetchAppliances() { this.appliances = []; this.applianceIds = []; @@ -66,6 +97,7 @@ export const useApplianceStore = defineStore('appliance', { const defaultApi = new DefaultApi(undefined, API_BASE_PATH); const response = await defaultApi.appliancesPost(newAppliance); const addedAppliance = response.data; + console.log("added appliance", addedAppliance) // Add the new appliance to the appliances array this.appliances.push(addedAppliance); return addedAppliance; diff --git a/webui/src/components/Stores/BladeStore.ts b/webui/src/components/Stores/BladeStore.ts index bddaee2..bdd30fe 100644 --- a/webui/src/components/Stores/BladeStore.ts +++ b/webui/src/components/Stores/BladeStore.ts @@ -18,6 +18,7 @@ export const useBladeStore = defineStore('blade', { addBladeError: null as unknown, deleteBladeError: null as unknown, resyncBladeError: null as unknown, + renameBladeError: null as unknown, }), actions: { async fetchBlades(applianceId: string) { @@ -68,6 +69,37 @@ export const useBladeStore = defineStore('blade', { } }, + async renameBlade(applianceId: string, bladeId: string, newBladeId: string) { + this.renameBladeError = ""; + + try { + const defaultApi = new DefaultApi(undefined, API_BASE_PATH); + const response = await defaultApi.bladesUpdateById(applianceId, bladeId, newBladeId); + + // Update the blades array + if (response) { + this.blades = this.blades.filter( + (blade) => blade.id !== bladeId + ); + this.blades.push(response.data); + } + + return response.data + } catch (error) { + if (axios.isAxiosError(error)) { + this.renameBladeError = error.message; + if (error.response) { + this.renameBladeError = error.response?.data.status.message + " (" + error.response?.request.status + ")"; + } + } + else { + this.renameBladeError = error; + } + console.error("Error:", error); + } + }, + + async resyncBlade(applianceId: string, bladeId: string) { this.resyncBladeError = ""; try { diff --git a/webui/src/components/Stores/HostStore.ts b/webui/src/components/Stores/HostStore.ts index b90e8f4..11f8607 100644 --- a/webui/src/components/Stores/HostStore.ts +++ b/webui/src/components/Stores/HostStore.ts @@ -18,6 +18,7 @@ export const useHostStore = defineStore('host', { addHostError: null as unknown, deleteHostError: null as unknown, resyncHostError: null as unknown, + renameHostError: null as unknown, hostIds: [] as string[], }), @@ -102,6 +103,37 @@ export const useHostStore = defineStore('host', { } }, + async renameHost(hostId: string, newHostId: string) { + this.renameHostError = ""; + + try { + const defaultApi = new DefaultApi(undefined, API_BASE_PATH); + const response = await defaultApi.hostsUpdateById(hostId, newHostId); + + // Update the hosts array + if (response) { + this.hosts = this.hosts.filter( + (host) => host.id !== hostId + ); + this.hosts.push(response.data); + } + + return response.data + } catch (error) { + if (axios.isAxiosError(error)) { + this.renameHostError = error.message; + if (error.response) { + this.renameHostError = error.response?.data.status.message + " (" + error.response?.request.status + ")"; + } + } + else { + this.renameHostError = error; + } + console.error("Error:", error); + } + }, + + async resyncHost(hostId: string) { this.resyncHostError = ""; try { From f254c8cffc91f86d3082b2a85895b7c1602e2f23 Mon Sep 17 00:00:00 2001 From: Scott Howe Date: Tue, 15 Oct 2024 11:40:55 -0600 Subject: [PATCH 12/12] doc: fix docker doc command variable name (#42) --- docs/DOCKER.md | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/docs/DOCKER.md b/docs/DOCKER.md index ed7d129..ae06ae6 100644 --- a/docs/DOCKER.md +++ b/docs/DOCKER.md @@ -17,7 +17,7 @@ If desired, the user can add `:vX.X.X` to the end of the command to obtain an ol To enable the webui launching during cfm-service startup, the user must provide the `-webui` flag in the command below. ```bash -docker run --restart unless-stopped --network=host --name --detach ghcr.io/seagate/cfm -webui -verbosity 4 +docker run --restart unless-stopped --network=host --name --detach ghcr.io/seagate/cfm -webui -verbosity 4 ``` By default, the cfm-service will be hosted at port 8080 and the webui will be hosted at port 3000. The user could change the port by input argument -Port and/or -webuiPort. The webui only works with --network=host mode. @@ -27,13 +27,13 @@ By default, the cfm-service will be hosted at port 8080 and the webui will be ho The cfm-service runtime logs can be viewed using ```bash -docker logs --follow +docker logs --follow ``` ## Stop and restart cfm-service ```bash -docker restart +docker restart ``` ## Excute CLI tool @@ -41,7 +41,7 @@ docker restart The user can interact with the running cfm docker container (running cfm-service) to utilize the cli tool. ```bash -docker exec -it ./cfm-cli +docker exec -it ./cfm-cli ``` NOTE: cfm-cli \ usage examples: @@ -53,8 +53,15 @@ docker exec -it cfm-container ./cfm-cli list appliances ## Customization -The developer could use the [DockerFile](../docker/Dockerfile) as a reference to build a new docker image with local changes +The developer could use the [DockerFile](../docker/Dockerfile) as a reference to build a new docker image using a local [cfm](https://github.com/Seagate/cfm) clone... ```bash -docker build --no-cache -t -f docker/Dockerfile . +cd /path/to/local/cfm +docker build --no-cache -t -f docker/Dockerfile . +``` + +...and then run those changes + +```bash +docker run --restart unless-stopped --network=host --name --detach -webui -verbosity 4 ```