diff --git a/go.mod b/go.mod index 5fe91ad8..3fdd0e1f 100644 --- a/go.mod +++ b/go.mod @@ -1,8 +1,8 @@ module github.com/longhorn/backing-image-manager -go 1.22.0 +go 1.22.2 -toolchain go1.22.6 +toolchain go1.23.0 require ( github.com/RoaringBitmap/roaring v1.9.4 @@ -10,7 +10,7 @@ require ( github.com/gorilla/mux v1.8.1 github.com/longhorn/backupstore v0.0.0-20240823072635-7afd6aa10d3e github.com/longhorn/go-common-libs v0.0.0-20240821134112-907f57efd48f - github.com/longhorn/longhorn-engine v1.6.0-dev-20231217.0.20240418025706-519598108463 + github.com/longhorn/longhorn-engine v1.7.0 github.com/longhorn/sparse-tools v0.0.0-20240729132735-18b207e459ff github.com/longhorn/types v0.0.0-20240725040629-473d671316c4 github.com/pkg/errors v0.9.1 @@ -34,17 +34,18 @@ require ( github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect - github.com/felixge/httpsnoop v1.0.3 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect github.com/gammazero/deque v0.2.1 // indirect github.com/gammazero/workerpool v1.1.3 // indirect github.com/go-logr/logr v1.4.2 // indirect - github.com/go-ole/go-ole v1.2.6 // indirect + github.com/go-ole/go-ole v1.3.0 // indirect github.com/godbus/dbus/v5 v5.1.0 // indirect github.com/google/uuid v1.6.0 // indirect github.com/gorilla/handlers v1.5.2 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect + github.com/longhorn/go-iscsi-helper v0.0.0-20240811043302-df8de353dd58 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/mitchellh/go-ps v1.0.0 // indirect github.com/moby/sys/mountinfo v0.7.1 // indirect @@ -52,7 +53,7 @@ require ( github.com/opencontainers/runc v1.1.13 // indirect github.com/opencontainers/runtime-spec v1.0.3-0.20220909204839-494a5a6aca78 // indirect github.com/pierrec/lz4/v4 v4.1.21 // indirect - github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect + github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/prometheus/client_golang v1.15.0 // indirect github.com/prometheus/client_model v0.3.0 // indirect github.com/prometheus/common v0.42.0 // indirect diff --git a/go.sum b/go.sum index 331add77..44bca84a 100644 --- a/go.sum +++ b/go.sum @@ -33,16 +33,17 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= -github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/gammazero/deque v0.2.1 h1:qSdsbG6pgp6nL7A0+K/B7s12mcCY/5l5SIUpMOl+dC0= github.com/gammazero/deque v0.2.1/go.mod h1:LFroj8x4cMYCukHJDbxFCkT+r9AndaJnFMuZDV34tuU= github.com/gammazero/workerpool v1.1.3 h1:WixN4xzukFoN0XSeXF6puqEqFTl2mECI9S6W44HWy9Q= github.com/gammazero/workerpool v1.1.3/go.mod h1:wPjyBLDbyKnUn2XwwyD3EEwo9dHutia9/fwNmSHWACc= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= -github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= +github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= @@ -77,8 +78,10 @@ github.com/longhorn/backupstore v0.0.0-20240823072635-7afd6aa10d3e h1:Kvt/BqiHKa github.com/longhorn/backupstore v0.0.0-20240823072635-7afd6aa10d3e/go.mod h1:N4cqNhSs4VUw9aGbO2OfyiIvJL7/L53hUrNiT73UN+U= github.com/longhorn/go-common-libs v0.0.0-20240821134112-907f57efd48f h1:hjqUs3WVodkzrWwlUMVsnKAlom3uohoNlhZBGLsRvQY= github.com/longhorn/go-common-libs v0.0.0-20240821134112-907f57efd48f/go.mod h1:Qv34svr/msf6XoUwnrltNBTwMhQljbHEhb5ZKWiRdxo= -github.com/longhorn/longhorn-engine v1.6.0-dev-20231217.0.20240418025706-519598108463 h1:KxddgUYC9InOhe8MxfbxzOL5v9q7f5DyThxQGGKvqVw= -github.com/longhorn/longhorn-engine v1.6.0-dev-20231217.0.20240418025706-519598108463/go.mod h1:WNiZl2l51I36/c8dewxkyWd0yBA5Anznzki0knKO88U= +github.com/longhorn/go-iscsi-helper v0.0.0-20240811043302-df8de353dd58 h1:fzLAnCLCecoUnsSYyyo7li5GD17xckyBl/zietxz168= +github.com/longhorn/go-iscsi-helper v0.0.0-20240811043302-df8de353dd58/go.mod h1:TobRDCXmF0Ni+jz6+nLJamw3uVu+gNDZoZre1JczGwc= +github.com/longhorn/longhorn-engine v1.7.0 h1:aScx5OBayf9mdrjIhD+mqnyorZj4T71baz+Cd4NdahI= +github.com/longhorn/longhorn-engine v1.7.0/go.mod h1:doPP8g43e2PusQPnVLUsW/OdCadqTAZAMIFEOWowSA4= github.com/longhorn/sparse-tools v0.0.0-20240729132735-18b207e459ff h1:gmdQDbnaGJ/zmrK+QJzSys8mH679os6i7vW/pOpRn1U= github.com/longhorn/sparse-tools v0.0.0-20240729132735-18b207e459ff/go.mod h1:iUJCZtOKG/9xv2rfrUAYZntFTzP5dZtvy4Kwe6dMcUc= github.com/longhorn/types v0.0.0-20240725040629-473d671316c4 h1:L2g0sIJ2fXt4BSFRYNnF6ObtKryCUFm9qLcCXHWssCk= @@ -106,8 +109,8 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= -github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= +github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= +github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= github.com/prometheus/client_golang v1.15.0 h1:5fCgGYogn0hFdhyhLbw7hEsWxufKtY9klyvdNfFlFhM= github.com/prometheus/client_golang v1.15.0/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk= @@ -166,6 +169,7 @@ golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= diff --git a/vendor/github.com/felixge/httpsnoop/.travis.yml b/vendor/github.com/felixge/httpsnoop/.travis.yml deleted file mode 100644 index bfc42120..00000000 --- a/vendor/github.com/felixge/httpsnoop/.travis.yml +++ /dev/null @@ -1,6 +0,0 @@ -language: go - -go: - - 1.6 - - 1.7 - - 1.8 diff --git a/vendor/github.com/felixge/httpsnoop/Makefile b/vendor/github.com/felixge/httpsnoop/Makefile index 2d84889a..4e12afdd 100644 --- a/vendor/github.com/felixge/httpsnoop/Makefile +++ b/vendor/github.com/felixge/httpsnoop/Makefile @@ -1,7 +1,7 @@ .PHONY: ci generate clean ci: clean generate - go test -v ./... + go test -race -v ./... generate: go generate . diff --git a/vendor/github.com/felixge/httpsnoop/README.md b/vendor/github.com/felixge/httpsnoop/README.md index ddcecd13..cf6b42f3 100644 --- a/vendor/github.com/felixge/httpsnoop/README.md +++ b/vendor/github.com/felixge/httpsnoop/README.md @@ -7,8 +7,8 @@ http.Handlers. Doing this requires non-trivial wrapping of the http.ResponseWriter interface, which is also exposed for users interested in a more low-level API. -[![GoDoc](https://godoc.org/github.com/felixge/httpsnoop?status.svg)](https://godoc.org/github.com/felixge/httpsnoop) -[![Build Status](https://travis-ci.org/felixge/httpsnoop.svg?branch=master)](https://travis-ci.org/felixge/httpsnoop) +[![Go Reference](https://pkg.go.dev/badge/github.com/felixge/httpsnoop.svg)](https://pkg.go.dev/github.com/felixge/httpsnoop) +[![Build Status](https://github.com/felixge/httpsnoop/actions/workflows/main.yaml/badge.svg)](https://github.com/felixge/httpsnoop/actions/workflows/main.yaml) ## Usage Example diff --git a/vendor/github.com/felixge/httpsnoop/capture_metrics.go b/vendor/github.com/felixge/httpsnoop/capture_metrics.go index b77cc7c0..bec7b71b 100644 --- a/vendor/github.com/felixge/httpsnoop/capture_metrics.go +++ b/vendor/github.com/felixge/httpsnoop/capture_metrics.go @@ -52,7 +52,7 @@ func (m *Metrics) CaptureMetrics(w http.ResponseWriter, fn func(http.ResponseWri return func(code int) { next(code) - if !headerWritten { + if !(code >= 100 && code <= 199) && !headerWritten { m.Code = code headerWritten = true } diff --git a/vendor/github.com/felixge/httpsnoop/wrap_generated_gteq_1.8.go b/vendor/github.com/felixge/httpsnoop/wrap_generated_gteq_1.8.go index 31cbdfb8..101cedde 100644 --- a/vendor/github.com/felixge/httpsnoop/wrap_generated_gteq_1.8.go +++ b/vendor/github.com/felixge/httpsnoop/wrap_generated_gteq_1.8.go @@ -1,5 +1,5 @@ // +build go1.8 -// Code generated by "httpsnoop/codegen"; DO NOT EDIT +// Code generated by "httpsnoop/codegen"; DO NOT EDIT. package httpsnoop diff --git a/vendor/github.com/felixge/httpsnoop/wrap_generated_lt_1.8.go b/vendor/github.com/felixge/httpsnoop/wrap_generated_lt_1.8.go index ab99c07c..e0951df1 100644 --- a/vendor/github.com/felixge/httpsnoop/wrap_generated_lt_1.8.go +++ b/vendor/github.com/felixge/httpsnoop/wrap_generated_lt_1.8.go @@ -1,5 +1,5 @@ // +build !go1.8 -// Code generated by "httpsnoop/codegen"; DO NOT EDIT +// Code generated by "httpsnoop/codegen"; DO NOT EDIT. package httpsnoop diff --git a/vendor/github.com/go-ole/go-ole/SECURITY.md b/vendor/github.com/go-ole/go-ole/SECURITY.md new file mode 100644 index 00000000..dac28152 --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/SECURITY.md @@ -0,0 +1,13 @@ +# Security Policy + +## Supported Versions + +Security updates are applied only to the latest release. + +## Reporting a Vulnerability + +If you have discovered a security vulnerability in this project, please report it privately. **Do not disclose it as a public issue.** This gives us time to work with you to fix the issue before public exposure, reducing the chance that the exploit will be used before a patch is released. + +Please disclose it at [security advisory](https://github.com/go-ole/go-ole/security/advisories/new). + +This project is maintained by a team of volunteers on a reasonable-effort basis. As such, please give us at least 90 days to work on a fix before public exposure. diff --git a/vendor/github.com/go-ole/go-ole/appveyor.yml b/vendor/github.com/go-ole/go-ole/appveyor.yml index 0d557ac2..8df7fa26 100644 --- a/vendor/github.com/go-ole/go-ole/appveyor.yml +++ b/vendor/github.com/go-ole/go-ole/appveyor.yml @@ -6,14 +6,9 @@ version: "1.3.0.{build}-alpha-{branch}" -os: Windows Server 2012 R2 +os: Visual Studio 2019 -branches: - only: - - master - - v1.2 - - v1.1 - - v1.0 +build: off skip_tags: true @@ -21,20 +16,40 @@ clone_folder: c:\gopath\src\github.com\go-ole\go-ole environment: GOPATH: c:\gopath - matrix: - - GOARCH: amd64 - GOVERSION: 1.5 - GOROOT: c:\go - DOWNLOADPLATFORM: "x64" + GOROOT: c:\go + DOWNLOADPLATFORM: "x64" -install: - - choco install mingw - - SET PATH=c:\tools\mingw64\bin;%PATH% +before_test: # - Download COM Server - ps: Start-FileDownload "https://github.com/go-ole/test-com-server/releases/download/v1.0.2/test-com-server-${env:DOWNLOADPLATFORM}.zip" - 7z e test-com-server-%DOWNLOADPLATFORM%.zip -oc:\gopath\src\github.com\go-ole\go-ole > NUL - c:\gopath\src\github.com\go-ole\go-ole\build\register-assembly.bat - # - set + +test_script: + - go test -v -cover ./... + # go vet has false positives on unsafe.Pointer with windows/sys. Disabling since it is recommended to use go test instead. + # - go vet ./... + +branches: + only: + - master + - v1.2 + - v1.1 + - v1.0 + +matrix: + allow_failures: + - environment: + GOROOT: C:\go-x86 + DOWNLOADPLATFORM: "x86" + - environment: + GOROOT: C:\go118 + DOWNLOADPLATFORM: "x64" + - environment: + GOROOT: C:\go118-x86 + DOWNLOADPLATFORM: "x86" + +install: - go version - go env - go get -u golang.org/x/tools/cmd/cover @@ -45,10 +60,9 @@ build_script: - cd c:\gopath\src\github.com\go-ole\go-ole - go get -v -t ./... - go build - - go test -v -cover ./... # disable automatic tests -test: off +test: on # disable deployment deploy: off diff --git a/vendor/github.com/go-ole/go-ole/com.go b/vendor/github.com/go-ole/go-ole/com.go index a9bef150..cabbac01 100644 --- a/vendor/github.com/go-ole/go-ole/com.go +++ b/vendor/github.com/go-ole/go-ole/com.go @@ -11,6 +11,7 @@ import ( var ( procCoInitialize = modole32.NewProc("CoInitialize") procCoInitializeEx = modole32.NewProc("CoInitializeEx") + procCoInitializeSecurity = modole32.NewProc("CoInitializeSecurity") procCoUninitialize = modole32.NewProc("CoUninitialize") procCoCreateInstance = modole32.NewProc("CoCreateInstance") procCoTaskMemFree = modole32.NewProc("CoTaskMemFree") @@ -37,6 +38,9 @@ var ( procDispatchMessageW = moduser32.NewProc("DispatchMessageW") ) +// This is to enable calling COM Security initialization multiple times +var bSecurityInit bool = false + // coInitialize initializes COM library on current thread. // // MSDN documentation suggests that this function should not be called. Call @@ -68,6 +72,35 @@ func coInitializeEx(coinit uint32) (err error) { return } +// coInitializeSecurity: Registers security and sets the default security values +// for the process. +func coInitializeSecurity(cAuthSvc int32, + dwAuthnLevel uint32, + dwImpLevel uint32, + dwCapabilities uint32) (err error) { + // Check COM Security initialization has done previously + if !bSecurityInit { + // https://learn.microsoft.com/en-us/windows/win32/api/combaseapi/nf-combaseapi-coinitializesecurity + hr, _, _ := procCoInitializeSecurity.Call( + uintptr(0), // Allow *all* VSS writers to communicate back! + uintptr(cAuthSvc), // Default COM authentication service + uintptr(0), // Default COM authorization service + uintptr(0), // Reserved parameter + uintptr(dwAuthnLevel), // Strongest COM authentication level + uintptr(dwImpLevel), // Minimal impersonation abilities + uintptr(0), // Default COM authentication settings + uintptr(dwCapabilities), // Cloaking + uintptr(0)) // eserved parameter + if hr != 0 { + err = NewError(hr) + } else { + // COM Security initialization done make global flag true. + bSecurityInit = true + } + } + return +} + // CoInitialize initializes COM library on current thread. // // MSDN documentation suggests that this function should not be called. Call @@ -96,6 +129,15 @@ func CoUninitialize() { procCoUninitialize.Call() } +// CoInitializeSecurity: Registers security and sets the default security values +// for the process. +func CoInitializeSecurity(cAuthSvc int32, + dwAuthnLevel uint32, + dwImpLevel uint32, + dwCapabilities uint32) (err error) { + return coInitializeSecurity(cAuthSvc, dwAuthnLevel, dwImpLevel, dwCapabilities) +} + // CoTaskMemFree frees memory pointer. func CoTaskMemFree(memptr uintptr) { procCoTaskMemFree.Call(memptr) diff --git a/vendor/github.com/go-ole/go-ole/idispatch_windows.go b/vendor/github.com/go-ole/go-ole/idispatch_windows.go index b399f047..649c0734 100644 --- a/vendor/github.com/go-ole/go-ole/idispatch_windows.go +++ b/vendor/github.com/go-ole/go-ole/idispatch_windows.go @@ -1,3 +1,4 @@ +//go:build windows // +build windows package ole @@ -92,7 +93,7 @@ func invoke(disp *IDispatch, dispid int32, dispatch int16, params ...interface{} case int8: vargs[n] = NewVariant(VT_I1, int64(v.(int8))) case *int8: - vargs[n] = NewVariant(VT_I1|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*uint8))))) + vargs[n] = NewVariant(VT_I1|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*int8))))) case int16: vargs[n] = NewVariant(VT_I2, int64(v.(int16))) case *int16: diff --git a/vendor/github.com/go-ole/go-ole/variant.go b/vendor/github.com/go-ole/go-ole/variant.go index 967a23fe..a2c8402f 100644 --- a/vendor/github.com/go-ole/go-ole/variant.go +++ b/vendor/github.com/go-ole/go-ole/variant.go @@ -99,7 +99,7 @@ func (v *VARIANT) Value() interface{} { case VT_DISPATCH: return v.ToIDispatch() case VT_BOOL: - return v.Val != 0 + return (v.Val & 0xffff) != 0 } return nil } diff --git a/vendor/github.com/longhorn/go-iscsi-helper/LICENSE b/vendor/github.com/longhorn/go-iscsi-helper/LICENSE new file mode 100644 index 00000000..8dada3ed --- /dev/null +++ b/vendor/github.com/longhorn/go-iscsi-helper/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/longhorn/go-iscsi-helper/iscsi/initiator.go b/vendor/github.com/longhorn/go-iscsi-helper/iscsi/initiator.go new file mode 100644 index 00000000..abe4c77b --- /dev/null +++ b/vendor/github.com/longhorn/go-iscsi-helper/iscsi/initiator.go @@ -0,0 +1,367 @@ +package iscsi + +import ( + "bufio" + "fmt" + "path/filepath" + "strconv" + "strings" + "time" + + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + + lhns "github.com/longhorn/go-common-libs/ns" + lhtypes "github.com/longhorn/go-common-libs/types" +) + +var ( + DeviceWaitRetryCounts = 10 + DeviceWaitRetryInterval = 1 * time.Second + + ScsiNodesDirs = []string{ + "/etc/iscsi/nodes/", + "/var/lib/iscsi/nodes/", + } +) + +const ( + iscsiBinary = "iscsiadm" + scanModeManual = "manual" + scanModeAuto = "auto" + ScanTimeout = 10 * time.Second +) + +func CheckForInitiatorExistence(nsexec *lhns.Executor) error { + opts := []string{ + "--version", + } + _, err := nsexec.Execute(nil, iscsiBinary, opts, lhtypes.ExecuteDefaultTimeout) + return err +} + +func UpdateScsiDeviceTimeout(devName string, timeout int64, nsexec *lhns.Executor) error { + deviceTimeoutFile := filepath.Join("/sys/block", devName, "device", "timeout") + return lhns.WriteFile(deviceTimeoutFile, fmt.Sprint(timeout)) +} + +func UpdateIscsiDeviceAbortTimeout(target string, timeout int64, nsexec *lhns.Executor) error { + opts := []string{ + "-m", "node", + "-T", target, + "-o", "update", + "-n", "node.session.err_timeo.abort_timeout", + "-v", strconv.FormatInt(timeout, 10), + } + _, err := nsexec.Execute(nil, iscsiBinary, opts, lhtypes.ExecuteDefaultTimeout) + return err +} + +func DiscoverTarget(ip, target string, nsexec *lhns.Executor) error { + opts := []string{ + "-m", "discovery", + "-t", "sendtargets", + "-p", ip, + } + output, err := nsexec.Execute(nil, iscsiBinary, opts, lhtypes.ExecuteDefaultTimeout) + if err != nil { + return err + } + // Sometime iscsiadm won't return error but showing e.g.: + // iscsiadm: Could not stat /etc/iscsi/nodes//,3260,-1/default to + // delete node: No such file or directory\n\niscsiadm: Could not + // add/update [tcp:[hw=,ip=,net_if=,iscsi_if=default] 172.18.0.5,3260,1 + // iqn.2019-10.io.longhorn:vol9]\n172.18.0.5:3260,1 + // iqn.2019-10.io.longhorn:vol9\n" + if strings.Contains(output, "Could not") { + return fmt.Errorf("cannot discover target: %s", output) + } + if !strings.Contains(output, target) { + return fmt.Errorf("cannot find target %s in discovered targets %s", target, output) + } + return nil +} + +func DeleteDiscoveredTarget(ip, target string, nsexec *lhns.Executor) error { + opts := []string{ + "-m", "node", + "-o", "delete", + "-T", target, + } + if ip != "" { + opts = append(opts, "-p", ip) + } + _, err := nsexec.Execute(nil, iscsiBinary, opts, lhtypes.ExecuteDefaultTimeout) + return err +} + +func IsTargetDiscovered(ip, target string, nsexec *lhns.Executor) bool { + opts := []string{ + "-m", "node", + "-T", target, + } + if ip != "" { + opts = append(opts, "-p", ip) + } + _, err := nsexec.Execute(nil, iscsiBinary, opts, lhtypes.ExecuteDefaultTimeout) + return err == nil +} + +func LoginTarget(ip, target string, nsexec *lhns.Executor) error { + opts := []string{ + "-m", "node", + "-T", target, + "-p", ip, + "--login", + } + _, err := nsexec.Execute(nil, iscsiBinary, opts, lhtypes.ExecuteDefaultTimeout) + if err != nil { + return err + } + + scanMode, err := getIscsiNodeSessionScanMode(ip, target, nsexec) + if err != nil { + return errors.Wrap(err, "Failed to get node.session.scan mode") + } + + if scanMode == scanModeManual { + logrus.Infof("Manually rescan LUNs of the target %v:%v", target, ip) + if err := manualScanSession(ip, target, nsexec); err != nil { + return errors.Wrapf(err, "failed to manually rescan iscsi session of target %v:%v", target, ip) + } + } else { + logrus.Infof("default: automatically rescan all LUNs of all iscsi sessions") + } + + return nil +} + +// LogoutTarget will logout all sessions if ip == "" +func LogoutTarget(ip, target string, nsexec *lhns.Executor) error { + opts := []string{ + "-m", "node", + "-T", target, + "--logout", + } + if ip != "" { + opts = append(opts, "-p", ip) + } + _, err := nsexec.Execute(nil, iscsiBinary, opts, lhtypes.ExecuteDefaultTimeout) + return err +} + +func GetDevice(ip, target string, lun int, nsexec *lhns.Executor) (*lhtypes.BlockDeviceInfo, error) { + var err error + + var dev *lhtypes.BlockDeviceInfo + for i := 0; i < DeviceWaitRetryCounts; i++ { + dev, err = findScsiDevice(ip, target, lun, nsexec) + if err == nil { + break + } + time.Sleep(DeviceWaitRetryInterval) + } + if err != nil { + return nil, err + } + return dev, nil +} + +// IsTargetLoggedIn check all portals if ip == "" +func IsTargetLoggedIn(ip, target string, nsexec *lhns.Executor) bool { + opts := []string{ + "-m", "session", + } + + output, err := nsexec.Execute(nil, iscsiBinary, opts, lhtypes.ExecuteDefaultTimeout) + if err != nil { + return false + } + /* It will looks like: + tcp: [463] 172.17.0.2:3260,1 iqn.2019-10.io.longhorn:test-volume + or: + tcp: [463] 172.17.0.2:3260,1 iqn.2019-10.io.longhorn:test-volume (non-flash) + */ + found := false + scanner := bufio.NewScanner(strings.NewReader(output)) + for scanner.Scan() { + line := scanner.Text() + if strings.Contains(line, ip+":") { + if strings.HasSuffix(line, " "+target) || + strings.Contains(scanner.Text(), " "+target+" ") { + found = true + break + } + } + } + + return found +} + +func manualScanSession(ip, target string, nsexec *lhns.Executor) error { + opts := []string{ + "-m", "node", + "-T", target, + "-p", ip, + "--rescan", + } + _, err := nsexec.Execute(nil, iscsiBinary, opts, ScanTimeout) + return err +} + +func getIscsiNodeSessionScanMode(ip, target string, nsexec *lhns.Executor) (string, error) { + opts := []string{ + "-m", "node", + "-T", target, + "-p", ip, + "-o", "show", + } + output, err := nsexec.Execute(nil, iscsiBinary, opts, ScanTimeout) + if err != nil { + return "", err + } + if strings.Contains(output, "node.session.scan = manual") { + return scanModeManual, nil + } + return scanModeAuto, nil +} + +func findScsiDevice(ip, target string, lun int, nsexec *lhns.Executor) (*lhtypes.BlockDeviceInfo, error) { + name := "" + + opts := []string{ + "-m", "session", + "-P", "3", + } + output, err := nsexec.Execute(nil, iscsiBinary, opts, lhtypes.ExecuteDefaultTimeout) + if err != nil { + return nil, err + } + /* + Now we got something like this in output, and need to parse it + Target: iqn.2019-10.io.longhorn:for.all (non-flash) + Current Portal: 172.17.0.2:3260,1 + Persistent Portal: 172.17.0.2:3260,1 + ... + Attached SCSI devices: + ... + scsi12 Channel 00 Id 0 Lun: 0 + scsi12 Channel 00 Id 0 Lun: 1 + Attached scsi disk sdb State: running + ... + Target: ... + */ + scanner := bufio.NewScanner(strings.NewReader(output)) + targetLine := "Target: " + target + ipLine := " " + ip + ":" + lunLine := "Lun: " + strconv.Itoa(lun) + diskPrefix := "Attached scsi disk" + stateLine := "State:" + + inTarget := false + inIP := false + inLun := false + for scanner.Scan() { + /* Target line can be: + Target: iqn.2019-10.io.longhorn:for.all (non-flash) + or: + Target: iqn.2019-10.io.longhorn:for.all + */ + if !inTarget && + (strings.Contains(scanner.Text(), targetLine+" ") || + strings.HasSuffix(scanner.Text(), targetLine)) { + inTarget = true + continue + } + if inTarget && strings.Contains(scanner.Text(), ipLine) { + inIP = true + continue + } + if inIP && strings.Contains(scanner.Text(), lunLine) { + inLun = true + continue + } + // The line we need + if inLun { + line := scanner.Text() + if !strings.Contains(line, diskPrefix) { + return nil, fmt.Errorf("invalid output format, cannot find disk in: %s\n %s", line, output) + } + line = strings.TrimSpace(strings.Split(line, stateLine)[0]) + line = strings.TrimPrefix(line, diskPrefix) + name = strings.TrimSpace(line) + break + } + } + + if name == "" { + return nil, fmt.Errorf("cannot find iSCSI device") + } + + // TODO: replace with namespace joiner + // now that we know the device is mapped, we can get it's (major:minor) + devices, err := lhns.GetSystemBlockDevices() + if err != nil { + return nil, err + } + + dev, known := devices[name] + if !known { + return nil, fmt.Errorf("cannot find kernel device for iSCSI device: %s", name) + } + + return &dev, nil +} + +func CleanupScsiNodes(target string) error { + for _, dir := range ScsiNodesDirs { + if _, err := lhns.GetFileInfo(dir); err != nil { + continue + } + + targetDir := filepath.Join(dir, target) + if _, err := lhns.GetFileInfo(targetDir); err != nil { + continue + } + + // Remove all empty files in the directory + emptyFilePaths, err := lhns.GetEmptyFiles(targetDir) + if err != nil { + return err + } + + for _, emptyFilePath := range emptyFilePaths { + err := lhns.DeletePath(emptyFilePath) + if err != nil { + return errors.Wrapf(err, "failed to clean up empty iSCSI node file %v", emptyFilePath) + } + } + + // Try to remove the upper level directory containing empty files. + // We don't mind if it fails. + dirContainEmptyFiles := make(map[string]bool) + for _, emptyFilePath := range emptyFilePaths { + dirContainEmptyFiles[filepath.Dir(emptyFilePath)] = true + } + for dir := range dirContainEmptyFiles { + err := lhns.DeleteDirectory(dir) + if err != nil { + logrus.WithError(err).Warnf("Failed to clean up iSCSI node directory %v", dir) + } + } + } + return nil +} + +func RescanTarget(ip, target string, nsexec *lhns.Executor) error { + opts := []string{ + "-m", "node", + "-T", target, + "-R", + } + if ip != "" { + opts = append(opts, "-p", ip) + } + _, err := nsexec.Execute(nil, iscsiBinary, opts, lhtypes.ExecuteDefaultTimeout) + return err +} diff --git a/vendor/github.com/longhorn/go-iscsi-helper/iscsi/target.go b/vendor/github.com/longhorn/go-iscsi-helper/iscsi/target.go new file mode 100644 index 00000000..fc564882 --- /dev/null +++ b/vendor/github.com/longhorn/go-iscsi-helper/iscsi/target.go @@ -0,0 +1,434 @@ +package iscsi + +import ( + "bufio" + "fmt" + "io" + "os" + "os/exec" + "strconv" + "strings" + "time" + + "github.com/pkg/errors" + + lhexec "github.com/longhorn/go-common-libs/exec" + lhtypes "github.com/longhorn/go-common-libs/types" +) + +var ( + TgtdRetryCounts = 5 + TgtdRetryInterval = 1 * time.Second +) + +const ( + tgtBinary = "tgtadm" + + maxTargetID = 4095 + + logFile = "/var/log/tgtd.log" +) + +// CreateTarget will create a iSCSI target using the name specified. If name is +// unspecified, a name will be generated. Notice the name must comply with iSCSI +// name format. +func CreateTarget(tid int, name string) error { + opts := []string{ + "--lld", "iscsi", + "--op", "new", + "--mode", "target", + "--tid", strconv.Itoa(tid), + "-T", name, + } + _, err := lhexec.NewExecutor().Execute(nil, tgtBinary, opts, lhtypes.ExecuteDefaultTimeout) + return err +} + +// DeleteTarget will remove a iSCSI target specified by tid +func DeleteTarget(tid int) error { + opts := []string{ + "--lld", "iscsi", + "--op", "delete", + "--mode", "target", + "--tid", strconv.Itoa(tid), + } + _, err := lhexec.NewExecutor().Execute(nil, tgtBinary, opts, lhtypes.ExecuteDefaultTimeout) + return err +} + +// AddLunBackedByFile will add a LUN in an existing target, which backing by +// specified file. +func AddLunBackedByFile(tid int, lun int, backingFile string) error { + opts := []string{ + "--lld", "iscsi", + "--op", "new", + "--mode", "logicalunit", + "--tid", strconv.Itoa(tid), + "--lun", strconv.Itoa(lun), + "-b", backingFile, + } + _, err := lhexec.NewExecutor().Execute(nil, tgtBinary, opts, lhtypes.ExecuteDefaultTimeout) + return err +} + +// AddLun will add a LUN in an existing target, which backing by +// specified file, using AIO backing-store +func AddLun(tid int, lun int, backingFile string, bstype string, bsopts string) error { + if !CheckTargetForBackingStore(bstype) { + return fmt.Errorf("backing-store %s is not supported", bstype) + } + opts := []string{ + "--lld", "iscsi", + "--op", "new", + "--mode", "logicalunit", + "--tid", strconv.Itoa(tid), + "--lun", strconv.Itoa(lun), + "-b", backingFile, + "--bstype", bstype, + } + if bsopts != "" { + opts = append(opts, "--bsopts", bsopts) + } + _, err := lhexec.NewExecutor().Execute(nil, tgtBinary, opts, lhtypes.ExecuteDefaultTimeout) + return err +} + +// UpdateLun will update parameters for the LUN +func UpdateLun(tid int, lun int, params map[string]string) error { + opts := []string{ + "--lld", "iscsi", + "--op", "update", + "--mode", "logicalunit", + "--tid", strconv.Itoa(tid), + "--lun", strconv.Itoa(lun), + } + if len(params) != 0 { + paramStr := "" + for k, v := range params { + paramStr += fmt.Sprintf("%s=%s,", k, v) + } + opts = append(opts, "--params", strings.TrimSuffix(paramStr, ",")) + } + _, err := lhexec.NewExecutor().Execute(nil, tgtBinary, opts, lhtypes.ExecuteDefaultTimeout) + return err +} + +// SetLunThinProvisioning will set param thin_provisioning to true for the LUN +func SetLunThinProvisioning(tid int, lun int) error { + return UpdateLun(tid, lun, map[string]string{"thin_provisioning": "1"}) +} + +// DisableWriteCache will set param write-cache to false for the LUN +func DisableWriteCache(tid int, lun int) error { + // Mode page 8 is the caching mode page + // Refer to "Caching Mode page (08h)" in SCSI Commands Reference Manual for more information. + // https://www.seagate.com/files/staticfiles/support/docs/manual/Interface%20manuals/100293068j.pdf + // https://github.com/fujita/tgt/blob/master/scripts/tgt-admin#L418 + return UpdateLun(tid, lun, map[string]string{"mode_page": "8:0:18:0x10:0:0xff:0xff:0:0:0xff:0xff:0xff:0xff:0x80:0x14:0:0:0:0:0:0"}) +} + +// DeleteLun will remove a LUN from an target +func DeleteLun(tid int, lun int) error { + opts := []string{ + "--lld", "iscsi", + "--op", "delete", + "--mode", "logicalunit", + "--tid", strconv.Itoa(tid), + "--lun", strconv.Itoa(lun), + } + _, err := lhexec.NewExecutor().Execute(nil, tgtBinary, opts, lhtypes.ExecuteDefaultTimeout) + return err +} + +// ExpandLun will update the size for the LUN. +// This is valid only for the customized tgt https://github.com/rancher/tgt/ +func ExpandLun(tid, lun int, size int64) error { + opts := []string{ + "--lld", "iscsi", + "--op", "update", + "--mode", "logicalunit", + "--tid", strconv.Itoa(tid), + "--lun", strconv.Itoa(lun), + "--params", fmt.Sprintf("bsopts=size=%d", size), + } + _, err := lhexec.NewExecutor().Execute(nil, tgtBinary, opts, lhtypes.ExecuteDefaultTimeout) + return err +} + +// BindInitiator will add permission to allow certain initiator(s) to connect to +// certain target. "ALL" is a special initiator which is the wildcard +func BindInitiator(tid int, initiator string) error { + opts := []string{ + "--lld", "iscsi", + "--op", "bind", + "--mode", "target", + "--tid", strconv.Itoa(tid), + "-I", initiator, + } + _, err := lhexec.NewExecutor().Execute(nil, tgtBinary, opts, lhtypes.ExecuteDefaultTimeout) + return err +} + +// UnbindInitiator will remove permission to allow certain initiator(s) to connect to +// certain target. +func UnbindInitiator(tid int, initiator string) error { + opts := []string{ + "--lld", "iscsi", + "--op", "unbind", + "--mode", "target", + "--tid", strconv.Itoa(tid), + "-I", initiator, + } + _, err := lhexec.NewExecutor().Execute(nil, tgtBinary, opts, lhtypes.ExecuteDefaultTimeout) + return err +} + +// StartDaemon will start tgtd daemon, prepare for further commands +func StartDaemon(debug bool) error { + if CheckTargetForBackingStore("rdwr") { + fmt.Fprintf(os.Stderr, "go-iscsi-helper: tgtd is already running\n") + return nil + } + + logf, err := os.OpenFile(logFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + if err != nil { + return err + } + go startDaemon(logf, debug) + + // Wait until daemon is up + daemonIsRunning := false + for i := 0; i < TgtdRetryCounts; i++ { + if CheckTargetForBackingStore("rdwr") { + daemonIsRunning = true + break + } + time.Sleep(TgtdRetryInterval) + } + if !daemonIsRunning { + return fmt.Errorf("failed to start tgtd daemon") + } + return nil +} + +func startDaemon(logf *os.File, debug bool) { + defer logf.Close() + + opts := []string{ + "-f", + } + if debug { + opts = append(opts, "-d", "1") + } + cmd := exec.Command("tgtd", opts...) + mw := io.MultiWriter(os.Stderr, logf) + cmd.Stdout = mw + cmd.Stderr = mw + if err := cmd.Run(); err != nil { + if CheckTargetForBackingStore("rdwr") { + fmt.Fprintf(mw, "go-iscsi-helper: tgtd is already running\n") + return + } + fmt.Fprintf(mw, "go-iscsi-helper: command failed: %v\n", err) + panic(err) + } + fmt.Fprintln(mw, "go-iscsi-helper: done") +} + +func CheckTargetForBackingStore(name string) bool { + opts := []string{ + "--lld", "iscsi", + "--op", "show", + "--mode", "system", + } + output, err := lhexec.NewExecutor().Execute(nil, tgtBinary, opts, lhtypes.ExecuteDefaultTimeout) + if err != nil { + return false + } + return strings.Contains(output, " "+name) +} + +// GetTargetTid If returned TID is -1, then target doesn't exist, but we won't +// return error +func GetTargetTid(name string) (int, error) { + opts := []string{ + "--lld", "iscsi", + "--op", "show", + "--mode", "target", + } + output, err := lhexec.NewExecutor().Execute(nil, tgtBinary, opts, lhtypes.ExecuteDefaultTimeout) + if err != nil { + return -1, err + } + /* Output will looks like: + Target 1: iqn.2016-08.com.example:a + System information: + ... + Target 2: iqn.2016-08.com.example:b + System information: + ... + */ + tid := -1 + scanner := bufio.NewScanner(strings.NewReader(output)) + for scanner.Scan() { + if strings.HasSuffix(scanner.Text(), " "+name) { + tidString := strings.Fields(strings.Split(scanner.Text(), ":")[0])[1] + tid, err = strconv.Atoi(tidString) + if err != nil { + return -1, errors.Wrapf(err, "BUG: Failed to parse %s", tidString) + } + break + } + } + return tid, nil +} + +func ShutdownTgtd() error { + // Step 1: Show all targets + showOpts := []string{"--op", "show", "--mode", "target"} + output, err := lhexec.NewExecutor().Execute(nil, tgtBinary, showOpts, lhtypes.ExecuteDefaultTimeout) + if err != nil { + + return fmt.Errorf("failed to show targets: %v", err) + } + + // Step 2: Parse target IDs + scanner := bufio.NewScanner(strings.NewReader(output)) + var targetIDs []string + for scanner.Scan() { + line := scanner.Text() + if strings.HasPrefix(line, "Target") { + fields := strings.Fields(line) + if len(fields) >= 2 { + targetIDs = append(targetIDs, fields[1]) + } + } + } + + if err := scanner.Err(); err != nil { + return errors.Wrapf(err, "failed to parse targets") + } + + // Step 3: Delete each target + for _, tid := range targetIDs { + deleteOpts := []string{"--op", "delete", "--mode", "target", "--tid", tid} + _, err := lhexec.NewExecutor().Execute(nil, tgtBinary, deleteOpts, lhtypes.ExecuteDefaultTimeout) + if err != nil { + return errors.Wrapf(err, "failed to delete target %s", tid) + } + } + + return nil +} + +func GetTargetConnections(tid int) (map[string][]string, error) { + opts := []string{ + "--lld", "iscsi", + "--op", "show", + "--mode", "conn", + "--tid", strconv.Itoa(tid), + } + output, err := lhexec.NewExecutor().Execute(nil, tgtBinary, opts, lhtypes.ExecuteDefaultTimeout) + if err != nil { + return nil, err + } + /* Output will looks like: + Session: 11 + Connection: 0 + Initiator: iqn.2016-08.com.example:a + IP Address: 192.168.0.1 + Session: 12 + Connection: 1 + Initiator: iqn.2016-08.com.example:a + IP Address: 192.168.0.2 + ... + */ + res := map[string][]string{} + currentSIDString := "" + currentCIDStringList := []string{} + scanner := bufio.NewScanner(strings.NewReader(output)) + for scanner.Scan() { + line := strings.TrimSpace(scanner.Text()) + if strings.HasPrefix(line, "Session: ") { + if currentSIDString != "" { + res[currentSIDString] = currentCIDStringList + } + sidFields := strings.Split(line, ": ") + if len(sidFields) != 2 { + return nil, fmt.Errorf("failed to parse and get session id from line %v", line) + } + sidString := sidFields[1] + if _, err := strconv.Atoi(sidString); err != nil { + return nil, err + } + currentSIDString = sidString + currentCIDStringList = []string{} + } + if strings.HasPrefix(line, "Connection: ") { + cidFields := strings.Split(line, ": ") + if len(cidFields) != 2 { + return nil, fmt.Errorf("failed to parse and get connection id from line %v", line) + } + cidString := cidFields[1] + if _, err := strconv.Atoi(cidString); err != nil { + return nil, err + } + currentCIDStringList = append(currentCIDStringList, cidString) + } + } + if len(currentCIDStringList) != 0 { + res[currentSIDString] = currentCIDStringList + } + return res, nil +} + +func CloseConnection(tid int, sid, cid string) error { + opts := []string{ + "--lld", "iscsi", + "--op", "delete", + "--mode", "conn", + "--tid", strconv.Itoa(tid), + "--sid", sid, + "--cid", cid, + } + _, err := lhexec.NewExecutor().Execute(nil, tgtBinary, opts, lhtypes.ExecuteDefaultTimeout) + return err +} + +func FindNextAvailableTargetID() (int, error) { + existingTids := map[int]struct{}{} + opts := []string{ + "--lld", "iscsi", + "--op", "show", + "--mode", "target", + } + output, err := lhexec.NewExecutor().Execute(nil, tgtBinary, opts, lhtypes.ExecuteDefaultTimeout) + if err != nil { + return -1, err + } + /* Output will looks like: + Target 1: iqn.2016-08.com.example:a + System information: + ... + Target 2: iqn.2016-08.com.example:b + System information: + ... + */ + scanner := bufio.NewScanner(strings.NewReader(output)) + for scanner.Scan() { + if strings.HasPrefix(scanner.Text(), "Target ") { + tidString := strings.Fields(strings.Split(scanner.Text(), ":")[0])[1] + tid, err := strconv.Atoi(tidString) + if err != nil { + return -1, errors.Wrapf(err, "BUG: Failed to parse %s", tidString) + } + existingTids[tid] = struct{}{} + } + } + for i := 1; i < maxTargetID; i++ { + if _, exists := existingTids[i]; !exists { + return i, nil + } + } + return -1, fmt.Errorf("cannot find an available target ID") +} diff --git a/vendor/github.com/longhorn/go-iscsi-helper/iscsidev/iscsi.go b/vendor/github.com/longhorn/go-iscsi-helper/iscsidev/iscsi.go new file mode 100644 index 00000000..f66431e9 --- /dev/null +++ b/vendor/github.com/longhorn/go-iscsi-helper/iscsidev/iscsi.go @@ -0,0 +1,379 @@ +package iscsidev + +import ( + "fmt" + "strings" + "time" + + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + + "github.com/longhorn/go-iscsi-helper/iscsi" + "github.com/longhorn/go-iscsi-helper/types" + "github.com/longhorn/go-iscsi-helper/util" + + lhns "github.com/longhorn/go-common-libs/ns" + lhtypes "github.com/longhorn/go-common-libs/types" +) + +var ( + LockFile = "/var/run/longhorn-iscsi.lock" + LockTimeout = 120 * time.Second + + TargetLunID = 1 + + RetryCounts = 5 + RetryIntervalSCSI = 3 * time.Second + RetryIntervalTargetID = 500 * time.Millisecond +) + +type ScsiDeviceParameters struct { + ScsiTimeout int64 +} + +type IscsiDeviceParameters struct { + IscsiAbortTimeout int64 +} + +type Device struct { + Target string + KernelDevice *lhtypes.BlockDeviceInfo + + ScsiDeviceParameters + IscsiDeviceParameters + + BackingFile string + BSType string + BSOpts string + + targetID int + + nsexec *lhns.Executor +} + +func NewDevice(name, backingFile, bsType, bsOpts string, scsiTimeout, iscsiAbortTimeout int64) (*Device, error) { + namespaces := []lhtypes.Namespace{lhtypes.NamespaceMnt, lhtypes.NamespaceNet} + nsexec, err := lhns.NewNamespaceExecutor(util.ISCSIdProcess, lhtypes.HostProcDirectory, namespaces) + if err != nil { + return nil, err + } + + dev := &Device{ + Target: GetTargetName(name), + ScsiDeviceParameters: ScsiDeviceParameters{ + ScsiTimeout: scsiTimeout, + }, + IscsiDeviceParameters: IscsiDeviceParameters{ + IscsiAbortTimeout: iscsiAbortTimeout, + }, + BackingFile: backingFile, + BSType: bsType, + BSOpts: bsOpts, + nsexec: nsexec, + } + return dev, nil +} + +func Volume2ISCSIName(name string) string { + return strings.Replace(name, "_", ":", -1) +} + +func GetTargetName(volumeName string) string { + return "iqn.2019-10.io.longhorn:" + Volume2ISCSIName(volumeName) +} + +func (dev *Device) ReloadTargetID() error { + tid, err := iscsi.GetTargetTid(dev.Target) + if err != nil { + return err + } + dev.targetID = tid + return nil +} + +func (dev *Device) CreateTarget() (err error) { + // Start tgtd daemon if it's not already running + if err := iscsi.StartDaemon(false); err != nil { + return err + } + + tid := 0 + for i := 0; i < RetryCounts; i++ { + if tid, err = iscsi.FindNextAvailableTargetID(); err != nil { + return err + } + logrus.Infof("go-iscsi-helper: found available target id %v", tid) + err = iscsi.CreateTarget(tid, dev.Target) + if err == nil { + dev.targetID = tid + break + } + logrus.Infof("go-iscsi-helper: failed to use target id %v, retrying with a new target ID: err %v", tid, err) + time.Sleep(RetryIntervalTargetID) + continue + } + if err != nil { + return err + } + + if err := iscsi.AddLun(dev.targetID, TargetLunID, dev.BackingFile, dev.BSType, dev.BSOpts); err != nil { + return err + } + // Cannot modify the parameters for the LUNs during the adding stage + if err := iscsi.SetLunThinProvisioning(dev.targetID, TargetLunID); err != nil { + return err + } + // Longhorn reads and writes data with direct io rather than buffer io, so + // the write cache is actually disabled in the implementation. + // Explicitly disable the write cache for meeting the SCSI specification. + if err := iscsi.DisableWriteCache(dev.targetID, TargetLunID); err != nil { + return err + } + if err := iscsi.BindInitiator(dev.targetID, "ALL"); err != nil { + return err + } + return nil +} + +func (dev *Device) StartInitator() error { + lock := lhns.NewLock(LockFile, LockTimeout) + if err := lock.Lock(); err != nil { + return errors.Wrap(err, "failed to lock") + } + defer lock.Unlock() + + if err := iscsi.CheckForInitiatorExistence(dev.nsexec); err != nil { + return err + } + + localIP, err := util.GetIPToHost() + if err != nil { + return err + } + + // Setup initiator + for i := 0; i < RetryCounts; i++ { + err := iscsi.DiscoverTarget(localIP, dev.Target, dev.nsexec) + if iscsi.IsTargetDiscovered(localIP, dev.Target, dev.nsexec) { + break + } + + logrus.WithError(err).Warnf("Failed to discover") + // This is a trick to recover from the case. Remove the + // empty entries in /etc/iscsi/nodes/. If one of the entry + // is empty it will triggered the issue. + if err := iscsi.CleanupScsiNodes(dev.Target); err != nil { + logrus.WithError(err).Warnf("Failed to clean up nodes for %v", dev.Target) + } else { + logrus.Warnf("Nodes cleaned up for %v", dev.Target) + } + + time.Sleep(RetryIntervalSCSI) + } + if err := iscsi.UpdateIscsiDeviceAbortTimeout(dev.Target, dev.IscsiAbortTimeout, dev.nsexec); err != nil { + return err + } + if err := iscsi.LoginTarget(localIP, dev.Target, dev.nsexec); err != nil { + return err + } + if dev.KernelDevice, err = iscsi.GetDevice(localIP, dev.Target, TargetLunID, dev.nsexec); err != nil { + return err + } + if err := iscsi.UpdateScsiDeviceTimeout(dev.KernelDevice.Name, dev.ScsiTimeout, dev.nsexec); err != nil { + return err + } + + return nil +} + +// ReloadInitiator does nothing for the iSCSI initiator/target except for +// updating the timeout. It is mainly responsible for initializing the struct +// field `dev.KernelDevice`. +func (dev *Device) ReloadInitiator() error { + lock := lhns.NewLock(LockFile, LockTimeout) + if err := lock.Lock(); err != nil { + return errors.Wrap(err, "failed to lock") + } + defer lock.Unlock() + + if err := iscsi.CheckForInitiatorExistence(dev.nsexec); err != nil { + return err + } + + localIP, err := util.GetIPToHost() + if err != nil { + return err + } + + if err := iscsi.DiscoverTarget(localIP, dev.Target, dev.nsexec); err != nil { + return err + } + + if !iscsi.IsTargetDiscovered(localIP, dev.Target, dev.nsexec) { + return fmt.Errorf("failed to discover target %v for the initiator", dev.Target) + } + + if err := iscsi.UpdateIscsiDeviceAbortTimeout(dev.Target, dev.IscsiAbortTimeout, dev.nsexec); err != nil { + return err + } + if dev.KernelDevice, err = iscsi.GetDevice(localIP, dev.Target, TargetLunID, dev.nsexec); err != nil { + return err + } + + return iscsi.UpdateScsiDeviceTimeout(dev.KernelDevice.Name, dev.ScsiTimeout, dev.nsexec) +} + +func (dev *Device) StopInitiator() error { + lock := lhns.NewLock(LockFile, LockTimeout) + if err := lock.Lock(); err != nil { + return errors.Wrap(err, "failed to lock") + } + defer lock.Unlock() + + if err := LogoutTarget(dev.Target, dev.nsexec); err != nil { + return errors.Wrapf(err, "failed to logout target") + } + return nil +} + +func (dev *Device) RefreshInitiator() error { + lock := lhns.NewLock(LockFile, LockTimeout) + if err := lock.Lock(); err != nil { + return errors.Wrap(err, "failed to lock") + } + defer lock.Unlock() + + if err := iscsi.CheckForInitiatorExistence(dev.nsexec); err != nil { + return err + } + + ip, err := util.GetIPToHost() + if err != nil { + return err + } + + return iscsi.RescanTarget(ip, dev.Target, dev.nsexec) +} + +func LogoutTarget(target string, nsexec *lhns.Executor) error { + if err := iscsi.CheckForInitiatorExistence(nsexec); err != nil { + return err + } + if iscsi.IsTargetLoggedIn("", target, nsexec) { + var err error + loggingOut := false + + logrus.Infof("Shutting down iSCSI device for target %v", target) + for i := 0; i < RetryCounts; i++ { + // New IP may be different from the IP in the previous record. + // https://github.com/longhorn/longhorn/issues/1920 + err = iscsi.LogoutTarget("", target, nsexec) + // Ignore Not Found error + if err == nil || strings.Contains(err.Error(), "exit status 21") { + err = nil + break + } + // The timeout for response may return in the future, + // check session to know if it's logged out or not + if strings.Contains(err.Error(), "Timeout executing: ") { + loggingOut = true + break + } + time.Sleep(RetryIntervalSCSI) + } + // Wait for device to logout + if loggingOut { + logrus.Infof("Logging out iSCSI device timeout, waiting for logout complete") + for i := 0; i < RetryCounts; i++ { + if !iscsi.IsTargetLoggedIn("", target, nsexec) { + err = nil + break + } + time.Sleep(RetryIntervalSCSI) + } + } + if err != nil { + return errors.Wrapf(err, "failed to logout target") + } + /* + * Immediately delete target after logout may result in error: + * + * "Could not execute operation on all records: encountered + * iSCSI database failure" in iscsiadm + * + * This happens especially there are other iscsiadm db + * operations go on at the same time. + * Retry to workaround this issue. Also treat "exit status + * 21"(no record found) as valid result + */ + for i := 0; i < RetryCounts; i++ { + if !iscsi.IsTargetDiscovered("", target, nsexec) { + err = nil + break + } + + err = iscsi.DeleteDiscoveredTarget("", target, nsexec) + // Ignore Not Found error + if err == nil || strings.Contains(err.Error(), "exit status 21") { + err = nil + break + } + time.Sleep(RetryIntervalSCSI) + } + if err != nil { + return err + } + } + return nil +} + +func (dev *Device) DeleteTarget() error { + if tid, err := iscsi.GetTargetTid(dev.Target); err == nil && tid != -1 { + if tid != dev.targetID && dev.targetID != 0 { + logrus.Errorf("BUG: Invalid TID %v found for %v, was %v", tid, dev.Target, dev.targetID) + } + + logrus.Infof("Shutting down iSCSI target %v", dev.Target) + + // UnbindInitiator can return tgtadmSuccess, tgtadmAclNoexist or tgtadmNoTarget + // Target is deleted in the last step, so tgtadmNoTarget error should not occur here. + // Just ignore tgtadmAclNoexist and continue working on the remaining tasks. + if err := iscsi.UnbindInitiator(tid, "ALL"); err != nil { + if !strings.Contains(err.Error(), types.TgtadmAclNoexist) { + return err + } + logrus.WithError(err).Warnf("failed to unbind initiator target id %v", tid) + } + + sessionConnectionsMap, err := iscsi.GetTargetConnections(tid) + if err != nil { + return err + } + for sid, cidList := range sessionConnectionsMap { + for _, cid := range cidList { + if err := iscsi.CloseConnection(tid, sid, cid); err != nil { + return err + } + } + } + + if err := iscsi.DeleteLun(tid, TargetLunID); err != nil { + return err + } + + if err := iscsi.DeleteTarget(tid); err != nil { + return err + } + } + return nil +} + +func (dev *Device) UpdateScsiBackingStore(bsType, bsOpts string) error { + dev.BSType = bsType + dev.BSOpts = bsOpts + return nil +} + +func (dev *Device) ExpandTarget(size int64) error { + return iscsi.ExpandLun(dev.targetID, TargetLunID, size) +} diff --git a/vendor/github.com/longhorn/go-iscsi-helper/longhorndev/dev.go b/vendor/github.com/longhorn/go-iscsi-helper/longhorndev/dev.go new file mode 100644 index 00000000..17807382 --- /dev/null +++ b/vendor/github.com/longhorn/go-iscsi-helper/longhorndev/dev.go @@ -0,0 +1,462 @@ +package longhorndev + +import ( + "fmt" + "os" + "os/exec" + "path/filepath" + "sync" + "time" + + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + + "github.com/longhorn/go-iscsi-helper/iscsidev" + "github.com/longhorn/go-iscsi-helper/types" + "github.com/longhorn/go-iscsi-helper/util" +) + +const ( + SocketDirectory = "/var/run" + DevPath = "/dev/longhorn/" + + WaitInterval = time.Second + WaitCount = 30 +) + +type LonghornDevice struct { + *sync.RWMutex + name string //VolumeName + size int64 + frontend string + endpoint string + scsiTimeout int64 + iscsiAbortTimeout int64 + iscsiTargetRequestTimeout int64 + + scsiDevice *iscsidev.Device +} + +type DeviceService interface { + GetFrontend() string + SetFrontend(frontend string) error + UnsetFrontendCheck() error + UnsetFrontend() + GetEndpoint() string + Enabled() bool + + InitDevice() error + Start() error + Shutdown() error + PrepareUpgrade() error + FinishUpgrade() error + Expand(size int64) error +} + +type DeviceCreator interface { + NewDevice(name string, size int64, frontend string) (DeviceService, error) +} + +type LonghornDeviceCreator struct{} + +func (ldc *LonghornDeviceCreator) NewDevice(name string, size int64, frontend string, scsiTimeout, iscsiAbortTimeout, iscsiTargetRequestTimeout int64) (DeviceService, error) { + if name == "" || size == 0 { + return nil, fmt.Errorf("invalid parameter for creating Longhorn device") + } + dev := &LonghornDevice{ + RWMutex: &sync.RWMutex{}, + name: name, + size: size, + scsiTimeout: scsiTimeout, + iscsiAbortTimeout: iscsiAbortTimeout, + iscsiTargetRequestTimeout: iscsiTargetRequestTimeout, + } + if err := dev.SetFrontend(frontend); err != nil { + return nil, err + } + return dev, nil +} + +func (d *LonghornDevice) InitDevice() error { + d.Lock() + defer d.Unlock() + + if d.scsiDevice != nil { + return nil + } + + if err := d.initScsiDevice(); err != nil { + return err + } + + // Try to cleanup possible leftovers. + return d.shutdownFrontend() +} + +// call with lock hold +func (d *LonghornDevice) initScsiDevice() error { + bsOpts := fmt.Sprintf("size=%v;request_timeout=%v", d.size, d.iscsiTargetRequestTimeout) + scsiDev, err := iscsidev.NewDevice(d.name, d.GetSocketPath(), "longhorn", bsOpts, d.scsiTimeout, d.iscsiAbortTimeout) + if err != nil { + return err + } + d.scsiDevice = scsiDev + + return nil +} + +func (d *LonghornDevice) Start() error { + stopCh := make(chan struct{}) + if err := <-d.WaitForSocket(stopCh); err != nil { + return err + } + + return d.startScsiDevice(true) +} + +func (d *LonghornDevice) startScsiDevice(startScsiDevice bool) (err error) { + d.Lock() + defer d.Unlock() + + switch d.frontend { + case types.FrontendTGTBlockDev: + // If iSCSI device is not started here, e.g., device upgrade, + // d.scsiDevice.KernelDevice is nil. + if startScsiDevice { + if d.scsiDevice == nil { + return fmt.Errorf("there is no iSCSI device during the frontend %v starts", d.frontend) + } + if err := d.scsiDevice.CreateTarget(); err != nil { + return err + } + if err := d.scsiDevice.StartInitator(); err != nil { + return err + } + if err := d.createDev(); err != nil { + return err + } + logrus.Infof("device %v: iSCSI device %s created", d.name, d.scsiDevice.KernelDevice.Name) + } else { + if err := d.scsiDevice.ReloadTargetID(); err != nil { + return err + } + if err := d.scsiDevice.ReloadInitiator(); err != nil { + return err + } + logrus.Infof("device %v: iSCSI device %s reloaded the target and the initiator", d.name, d.scsiDevice.KernelDevice.Name) + } + + d.endpoint = d.getDev() + + case types.FrontendTGTISCSI: + if startScsiDevice { + if d.scsiDevice == nil { + return fmt.Errorf("there is no iSCSI device during the frontend %v starts", d.frontend) + } + if err := d.scsiDevice.CreateTarget(); err != nil { + return err + } + logrus.Infof("device %v: iSCSI target %s created", d.name, d.scsiDevice.Target) + } else { + if err := d.scsiDevice.ReloadTargetID(); err != nil { + return err + } + logrus.Infof("device %v: iSCSI target %s reloaded the target ID", d.name, d.scsiDevice.Target) + } + + d.endpoint = d.scsiDevice.Target + + default: + return fmt.Errorf("unknown frontend %v", d.frontend) + } + + logrus.Debugf("device %v: frontend start succeed", d.name) + return nil +} + +func (d *LonghornDevice) Shutdown() error { + d.Lock() + defer d.Unlock() + + if d.scsiDevice == nil { + return nil + } + + if err := d.shutdownFrontend(); err != nil { + return err + } + + d.scsiDevice = nil + d.endpoint = "" + + return nil +} + +// call with lock hold +func (d *LonghornDevice) shutdownFrontend() error { + switch d.frontend { + case types.FrontendTGTBlockDev: + dev := d.getDev() + if err := util.RemoveDevice(dev); err != nil { + return errors.Wrapf(err, "device %v: failed to remove device %s", d.name, dev) + } + if err := d.scsiDevice.StopInitiator(); err != nil { + return errors.Wrapf(err, "device %v: failed to stop iSCSI device", d.name) + } + if err := d.scsiDevice.DeleteTarget(); err != nil { + return errors.Wrapf(err, "device %v: failed to delete target %v", d.name, d.scsiDevice.Target) + } + logrus.Infof("device %v: iSCSI device %v shutdown", d.name, dev) + case types.FrontendTGTISCSI: + if err := d.scsiDevice.DeleteTarget(); err != nil { + return errors.Wrapf(err, "device %v: failed to delete target %v", d.name, d.scsiDevice.Target) + } + logrus.Infof("device %v: iSCSI target %v ", d.name, d.scsiDevice.Target) + case "": + logrus.Infof("device %v: skip shutdown frontend since it's not enabled", d.name) + default: + return fmt.Errorf("device %v: unknown frontend %v", d.name, d.frontend) + } + + return nil +} + +func (d *LonghornDevice) WaitForSocket(stopCh chan struct{}) chan error { + errCh := make(chan error) + go func(errCh chan error, stopCh chan struct{}) { + socket := d.GetSocketPath() + timeout := time.After(time.Duration(WaitCount) * WaitInterval) + ticker := time.NewTicker(WaitInterval) + defer ticker.Stop() + tick := ticker.C + for { + select { + case <-timeout: + errCh <- fmt.Errorf("device %v: wait for socket %v timed out", d.name, socket) + case <-tick: + if _, err := os.Stat(socket); err == nil { + errCh <- nil + return + } + logrus.Infof("device %v: waiting for socket %v to show up", d.name, socket) + case <-stopCh: + logrus.Infof("device %v: stop wait for socket routine", d.name) + return + } + } + }(errCh, stopCh) + + return errCh +} + +func (d *LonghornDevice) GetSocketPath() string { + return filepath.Join(SocketDirectory, "longhorn-"+d.name+".sock") +} + +// call with lock hold +func (d *LonghornDevice) getDev() string { + return filepath.Join(DevPath, d.name) +} + +// call with lock hold +func (d *LonghornDevice) createDev() error { + if _, err := os.Stat(DevPath); os.IsNotExist(err) { + if err := os.MkdirAll(DevPath, 0755); err != nil { + logrus.Fatalf("device %v: cannot create directory %v", d.name, DevPath) + } + } + + dev := d.getDev() + if _, err := os.Stat(dev); err == nil { + logrus.Warnf("Device %s already exists, clean it up", dev) + if err := util.RemoveDevice(dev); err != nil { + return errors.Wrapf(err, "cannot clean up block device file %v", dev) + } + } + + if err := util.DuplicateDevice(d.scsiDevice.KernelDevice, dev); err != nil { + return err + } + + logrus.Debugf("device %v: Device %s is ready", d.name, dev) + + return nil +} + +func (d *LonghornDevice) PrepareUpgrade() error { + if d.frontend == "" { + return nil + } + + if err := util.RemoveFile(d.GetSocketPath()); err != nil { + return errors.Wrapf(err, "failed to remove socket %v", d.GetSocketPath()) + } + return nil +} + +func (d *LonghornDevice) FinishUpgrade() (err error) { + if d.frontend == "" { + return nil + } + + stopCh := make(chan struct{}) + socketError := d.WaitForSocket(stopCh) + err = <-socketError + if err != nil { + err = errors.Wrap(err, "error waiting for the socket") + logrus.Error(err) + } + + close(stopCh) + close(socketError) + + if err != nil { + return err + } + + // TODO: Need to fix `ReloadSocketConnection` since it doesn't work for frontend `FrontendTGTISCSI`. + if err := d.ReloadSocketConnection(); err != nil { + return err + } + + d.Lock() + if err := d.initScsiDevice(); err != nil { + d.Unlock() + return err + } + d.Unlock() + + return d.startScsiDevice(false) +} + +func (d *LonghornDevice) ReloadSocketConnection() error { + d.RLock() + dev := d.getDev() + d.RUnlock() + + cmd := exec.Command("sg_raw", dev, "a6", "00", "00", "00", "00", "00") + if err := cmd.Run(); err != nil { + return errors.Wrapf(err, "failed to reload socket connection at %v", dev) + } + logrus.Infof("Reloaded completed for device %v", dev) + return nil +} + +func (d *LonghornDevice) SetFrontend(frontend string) error { + if frontend != types.FrontendTGTBlockDev && frontend != types.FrontendTGTISCSI && frontend != "" { + return fmt.Errorf("invalid frontend %v", frontend) + } + + d.Lock() + defer d.Unlock() + if d.frontend != "" { + if d.frontend != frontend { + return fmt.Errorf("engine frontend %v is already up and cannot be set to %v", d.frontend, frontend) + } + if d.scsiDevice != nil { + logrus.Infof("Engine frontend %v is already up", frontend) + return nil + } + // d.scsiDevice == nil + return fmt.Errorf("engine frontend had been set to %v, but its frontend cannot be started before engine manager shutdown its frontend", frontend) + } + + if d.scsiDevice != nil { + return fmt.Errorf("BUG: engine launcher frontend is empty but scsi device hasn't been cleanup in frontend start") + } + + d.frontend = frontend + + return nil +} + +func (d *LonghornDevice) UnsetFrontendCheck() error { + d.Lock() + defer d.Unlock() + + if d.scsiDevice == nil { + d.frontend = "" + logrus.Debugf("Engine frontend is already down") + return nil + } + + if d.frontend == "" { + return fmt.Errorf("BUG: engine launcher frontend is empty but scsi device hasn't been cleanup in frontend shutdown") + } + return nil +} + +func (d *LonghornDevice) UnsetFrontend() { + d.Lock() + defer d.Unlock() + + d.frontend = "" +} + +func (d *LonghornDevice) Enabled() bool { + d.RLock() + defer d.RUnlock() + return d.scsiDevice != nil +} + +func (d *LonghornDevice) GetEndpoint() string { + d.RLock() + defer d.RUnlock() + return d.endpoint +} + +func (d *LonghornDevice) GetFrontend() string { + d.RLock() + defer d.RUnlock() + return d.frontend +} + +func (d *LonghornDevice) Expand(size int64) (err error) { + d.Lock() + defer d.Unlock() + + if d.size > size { + return fmt.Errorf("device %v: cannot expand the device from size %v to a smaller size %v", d.name, d.size, size) + } else if d.size == size { + return nil + } + + defer func() { + if err == nil { + d.size = size + } + }() + + if d.scsiDevice == nil { + logrus.Info("Device: No need to do anything for the expansion since the frontend is shutdown") + return nil + } + if err := d.scsiDevice.UpdateScsiBackingStore("longhorn", fmt.Sprintf("size=%v", size)); err != nil { + return err + } + + switch d.frontend { + case types.FrontendTGTBlockDev: + logrus.Infof("Device %v: Expanding frontend %v target %v", d.name, d.frontend, d.scsiDevice.Target) + if err := d.scsiDevice.ExpandTarget(size); err != nil { + return fmt.Errorf("device %v: fail to expand target %v: %v", d.name, d.scsiDevice.Target, err) + } + logrus.Infof("Device %v: Refreshing/Rescanning frontend %v initiator for the expansion", d.name, d.frontend) + if err := d.scsiDevice.RefreshInitiator(); err != nil { + return fmt.Errorf("device %v: fail to refresh iSCSI initiator: %v", d.name, err) + } + logrus.Infof("Device %v: Expanded frontend %v size to %d", d.name, d.frontend, size) + case types.FrontendTGTISCSI: + logrus.Infof("Device %v: Frontend is expanding the target %v", d.name, d.scsiDevice.Target) + if err := d.scsiDevice.ExpandTarget(size); err != nil { + return fmt.Errorf("device %v: fail to expand target %v: %v", d.name, d.scsiDevice.Target, err) + } + logrus.Infof("Device %v: Expanded frontend %v size to %d, users need to refresh/rescan the initiator by themselves", d.name, d.frontend, size) + case "": + logrus.Infof("Device %v: skip expansion since the frontend not enabled", d.name) + default: + return fmt.Errorf("failed to expand device %v: unknown frontend %v", d.name, d.frontend) + } + + return nil +} diff --git a/vendor/github.com/longhorn/go-iscsi-helper/types/tgtadm_errors.go b/vendor/github.com/longhorn/go-iscsi-helper/types/tgtadm_errors.go new file mode 100644 index 00000000..1b7215c1 --- /dev/null +++ b/vendor/github.com/longhorn/go-iscsi-helper/types/tgtadm_errors.go @@ -0,0 +1,30 @@ +package types + +// errors are from tgt/usr/tgtadm_error.h and tgt/usr/tgtadm.c +const ( + TgtadmSuccess = "success" + TgtadmUnknown = "unknown error" + TgtadmNomem = "out of memory" + TgtadmNoDriver = "can't find the driver" + TgtadmNoTarget = "can't find the target" + TgtadmNoLun = "can't find the logical unit" + TgtadmNoSession = "can't find the session" + TgtadmNoConnection = "can't find the connection" + TgtadmNoBinding = "can't find the binding" + TgtadmTargetExist = "this target already exists" + TgtadmBindingExist = "this binding already exists" + TgtadmLunExist = "this logical unit number already exists" + TgtadmAclExist = "this access control rule already exists" + TgtadmAclNoexist = "this access control rule does not exist" + TgtadmUserExist = "this account already exists" + TgtadmNoUser = "can't find the account" + TgtadmTooManyUser = "too many accounts" + TgtadmInvalidRequest = "invalid request" + TgtadmOutAccountExist = "this target already has an outgoing account" + TgtadmTargetActive = "this target is still active" + TgtadmLunActive = "this logical unit is still active" + TgtadmDriverActive = "this driver is busy" + TgtadmUnsupportedOperation = "this operation isn't supported" + TgtadmUnknownParam = "unknown parameter" + TgtadmPreventRemoval = "this device has Prevent Removal set" +) diff --git a/vendor/github.com/longhorn/go-iscsi-helper/types/types.go b/vendor/github.com/longhorn/go-iscsi-helper/types/types.go new file mode 100644 index 00000000..d85a3c43 --- /dev/null +++ b/vendor/github.com/longhorn/go-iscsi-helper/types/types.go @@ -0,0 +1,6 @@ +package types + +const ( + FrontendTGTBlockDev = "tgt-blockdev" + FrontendTGTISCSI = "tgt-iscsi" +) diff --git a/vendor/github.com/longhorn/go-iscsi-helper/util/process.go b/vendor/github.com/longhorn/go-iscsi-helper/util/process.go new file mode 100644 index 00000000..54412f51 --- /dev/null +++ b/vendor/github.com/longhorn/go-iscsi-helper/util/process.go @@ -0,0 +1,18 @@ +package util + +import ( + "fmt" + + lhproc "github.com/longhorn/go-common-libs/proc" +) + +const ISCSIdProcess = "iscsid" + +func GetISCSIdNamespaceDirectory(procDir string) (string, error) { + pids, err := lhproc.GetProcessPIDs(ISCSIdProcess, procDir) + if err != nil { + return "", err + } + + return lhproc.GetNamespaceDirectory(procDir, fmt.Sprint(pids[0])), nil +} diff --git a/vendor/github.com/longhorn/go-iscsi-helper/util/util.go b/vendor/github.com/longhorn/go-iscsi-helper/util/util.go new file mode 100644 index 00000000..a51a2b70 --- /dev/null +++ b/vendor/github.com/longhorn/go-iscsi-helper/util/util.go @@ -0,0 +1,121 @@ +package util + +import ( + "fmt" + "net" + "os" + + "strings" + "time" + + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "golang.org/x/sys/unix" + + lhtypes "github.com/longhorn/go-common-libs/types" +) + +func getIPFromAddrs(addrs []net.Addr) string { + for _, addr := range addrs { + if ip, ok := addr.(*net.IPNet); ok && ip.IP.IsGlobalUnicast() { + return strings.Split(ip.IP.String(), "/")[0] + } + } + return "" +} + +func GetIPToHost() (string, error) { + ifaces, err := net.Interfaces() + if err != nil { + return "", err + } + // TODO: This is a workaround, we want to get the interface IP connect + // to the host, it's likely eth1 with one network attached to the host. + for _, iface := range ifaces { + if iface.Name == "eth1" { + addrs, err := iface.Addrs() + if err != nil { + return "", err + } + ip := getIPFromAddrs(addrs) + if ip != "" { + return ip, nil + } + } + } + // And there is no eth1, so get the first real ip + addrs, err := net.InterfaceAddrs() + if err != nil { + return "", err + } + ip := getIPFromAddrs(addrs) + if ip != "" { + return ip, nil + } + return "", fmt.Errorf("cannot find IP connect to the host") +} + +func RemoveFile(file string) error { + if _, err := os.Stat(file); os.IsNotExist(err) { + // file doesn't exist + return nil + } + + if err := remove(file); err != nil { + return errors.Wrapf(err, "failed to remove file %v", file) + } + + return nil +} + +func RemoveDevice(dev string) error { + if _, err := os.Stat(dev); err == nil { + if err := remove(dev); err != nil { + return errors.Wrapf(err, "failed to removing device %s", dev) + } + } + return nil +} + +func DuplicateDevice(dev *lhtypes.BlockDeviceInfo, dest string) error { + if err := mknod(dest, dev.Major, dev.Minor); err != nil { + return errors.Wrapf(err, "cannot create device node %s for device %s", dest, dev.Name) + } + if err := os.Chmod(dest, 0660); err != nil { + return errors.Wrapf(err, "cannot change permission of the device %s", dest) + } + // We use the group 6 by default because this is common group for disks + // See more at https://github.com/longhorn/longhorn/issues/8088#issuecomment-1982300242 + if err := os.Chown(dest, 0, 6); err != nil { + return errors.Wrapf(err, "cannot change ownership of the device %s", dest) + } + return nil +} + +func mknod(device string, major, minor int) error { + var fileMode os.FileMode = 0660 + fileMode |= unix.S_IFBLK + dev := int(unix.Mkdev(uint32(major), uint32(minor))) + + logrus.Infof("Creating device %s %d:%d", device, major, minor) + return unix.Mknod(device, uint32(fileMode), dev) +} + +func removeAsync(path string, done chan<- error) { + if err := os.Remove(path); err != nil && !os.IsNotExist(err) { + logrus.Errorf("Unable to remove: %v", path) + done <- err + } + done <- nil +} + +func remove(path string) error { + done := make(chan error) + go removeAsync(path, done) + select { + case err := <-done: + return err + case <-time.After(30 * time.Second): + return fmt.Errorf("timeout trying to delete %s", path) + } +} diff --git a/vendor/github.com/longhorn/longhorn-engine/pkg/replica/client/client.go b/vendor/github.com/longhorn/longhorn-engine/pkg/replica/client/client.go index 1ecb1d40..4fc36aac 100644 --- a/vendor/github.com/longhorn/longhorn-engine/pkg/replica/client/client.go +++ b/vendor/github.com/longhorn/longhorn-engine/pkg/replica/client/client.go @@ -92,7 +92,7 @@ func NewReplicaClient(address, volumeName, instanceName string) (*ReplicaClient, // for the longhorn-manager which executes these command as binaries invocations func (c *ReplicaClient) getReplicaServiceClient() (enginerpc.ReplicaServiceClient, error) { err := c.replicaServiceContext.once.Do(func() error { - cc, err := grpc.Dial(c.replicaServiceURL, grpc.WithTransportCredentials(insecure.NewCredentials()), + cc, err := grpc.NewClient(c.replicaServiceURL, grpc.WithTransportCredentials(insecure.NewCredentials()), interceptor.WithIdentityValidationClientInterceptor(c.volumeName, c.instanceName)) if err != nil { return err @@ -113,7 +113,7 @@ func (c *ReplicaClient) getReplicaServiceClient() (enginerpc.ReplicaServiceClien // for the longhorn-manager which executes these command as binaries invocations func (c *ReplicaClient) getSyncServiceClient() (enginerpc.SyncAgentServiceClient, error) { err := c.syncServiceContext.once.Do(func() error { - cc, err := grpc.Dial(c.syncAgentServiceURL, grpc.WithTransportCredentials(insecure.NewCredentials()), + cc, err := grpc.NewClient(c.syncAgentServiceURL, grpc.WithTransportCredentials(insecure.NewCredentials()), interceptor.WithIdentityValidationClientInterceptor(c.volumeName, c.instanceName)) if err != nil { return err @@ -442,12 +442,16 @@ func (c *ReplicaClient) RenameFile(oldFileName, newFileName string) error { return nil } -func (c *ReplicaClient) SendFile(from, host string, port int32, fileSyncHTTPClientTimeout int, fastSync bool) error { +func (c *ReplicaClient) SendFile(from, host string, port int32, fileSyncHTTPClientTimeout int, fastSync bool, grpcTimeoutSeconds int64) error { syncAgentServiceClient, err := c.getSyncServiceClient() if err != nil { return err } - ctx, cancel := context.WithTimeout(context.Background(), GRPCServiceLongTimeout) + grpcTimeout := GRPCServiceLongTimeout + if grpcTimeoutSeconds > 0 { + grpcTimeout = time.Second * time.Duration(grpcTimeoutSeconds) + } + ctx, cancel := context.WithTimeout(context.Background(), grpcTimeout) defer cancel() if _, err := syncAgentServiceClient.FileSend(ctx, &enginerpc.FileSendRequest{ @@ -502,21 +506,35 @@ func (c *ReplicaClient) LaunchReceiver(toFilePath string) (string, int32, error) return c.host, reply.Port, nil } -func (c *ReplicaClient) SyncFiles(fromAddress string, list []types.SyncFileInfo, fileSyncHTTPClientTimeout int, fastSync bool) error { +func (c *ReplicaClient) SyncFiles(fromAddress string, list []types.SyncFileInfo, fileSyncHTTPClientTimeout int, fastSync bool, grpcTimeoutSeconds int64, localSync *types.FileLocalSync) error { syncAgentServiceClient, err := c.getSyncServiceClient() if err != nil { return err } - ctx, cancel := context.WithTimeout(context.Background(), GRPCServiceLongTimeout) + grpcTimeout := GRPCServiceLongTimeout + if grpcTimeoutSeconds > 0 { + grpcTimeout = time.Second * time.Duration(grpcTimeoutSeconds) + } + ctx, cancel := context.WithTimeout(context.Background(), grpcTimeout) defer cancel() - if _, err := syncAgentServiceClient.FilesSync(ctx, &enginerpc.FilesSyncRequest{ + fileSyncRequest := &enginerpc.FilesSyncRequest{ FromAddress: fromAddress, ToHost: c.host, SyncFileInfoList: syncFileInfoListToSyncAgentGRPCFormat(list), FastSync: fastSync, FileSyncHttpClientTimeout: int32(fileSyncHTTPClientTimeout), - }); err != nil { + GrpcTimeoutSeconds: grpcTimeoutSeconds, + } + + if localSync != nil { + fileSyncRequest.LocalSync = &enginerpc.FileLocalSync{ + SourcePath: localSync.SourcePath, + TargetPath: localSync.TargetPath, + } + } + + if _, err := syncAgentServiceClient.FilesSync(ctx, fileSyncRequest); err != nil { return errors.Wrapf(err, "failed to sync files %+v from %v", list, fromAddress) } @@ -524,7 +542,7 @@ func (c *ReplicaClient) SyncFiles(fromAddress string, list []types.SyncFileInfo, } func (c *ReplicaClient) CreateBackup(backupName, snapshot, dest, volume, backingImageName, backingImageChecksum, - compressionMethod string, concurrentLimit int, storageClassName string, labels []string, credential map[string]string) (*enginerpc.BackupCreateResponse, error) { + compressionMethod string, concurrentLimit int, storageClassName string, labels []string, credential map[string]string, parameters map[string]string) (*enginerpc.BackupCreateResponse, error) { syncAgentServiceClient, err := c.getSyncServiceClient() if err != nil { return nil, err @@ -544,6 +562,7 @@ func (c *ReplicaClient) CreateBackup(backupName, snapshot, dest, volume, backing Labels: labels, Credential: credential, BackupName: backupName, + Parameters: parameters, }) if err != nil { return nil, errors.Wrapf(err, "failed to create backup to %v for volume %v", dest, volume) @@ -686,12 +705,16 @@ func (c *ReplicaClient) ReplicaRebuildStatus() (*enginerpc.ReplicaRebuildStatusR return status, nil } -func (c *ReplicaClient) CloneSnapshot(fromAddress, fromVolumeName, snapshotFileName string, exportBackingImageIfExist bool, fileSyncHTTPClientTimeout int) error { +func (c *ReplicaClient) CloneSnapshot(fromAddress, fromVolumeName, snapshotFileName string, exportBackingImageIfExist bool, fileSyncHTTPClientTimeout int, grpcTimeoutSeconds int64) error { syncAgentServiceClient, err := c.getSyncServiceClient() if err != nil { return err } - ctx, cancel := context.WithTimeout(context.Background(), GRPCServiceLongTimeout) + grpcTimeout := GRPCServiceLongTimeout + if grpcTimeoutSeconds > 0 { + grpcTimeout = time.Second * time.Duration(grpcTimeoutSeconds) + } + ctx, cancel := context.WithTimeout(context.Background(), grpcTimeout) defer cancel() if _, err := syncAgentServiceClient.SnapshotClone(ctx, &enginerpc.SnapshotCloneRequest{ diff --git a/vendor/github.com/longhorn/longhorn-engine/pkg/types/types.go b/vendor/github.com/longhorn/longhorn-engine/pkg/types/types.go index d93911cd..3e02bbb7 100644 --- a/vendor/github.com/longhorn/longhorn-engine/pkg/types/types.go +++ b/vendor/github.com/longhorn/longhorn-engine/pkg/types/types.go @@ -138,7 +138,8 @@ type Replica struct { } type ReplicaSalvageInfo struct { - LastModifyTime int64 + Address string + LastModifyTime time.Time HeadFileSize int64 } @@ -202,3 +203,8 @@ func GRPCReplicaModeToReplicaMode(replicaMode enginerpc.ReplicaMode) Mode { } return ERR } + +type FileLocalSync struct { + SourcePath string + TargetPath string +} diff --git a/vendor/github.com/longhorn/longhorn-engine/pkg/util/fsfreeze.go b/vendor/github.com/longhorn/longhorn-engine/pkg/util/fsfreeze.go new file mode 100644 index 00000000..64028365 --- /dev/null +++ b/vendor/github.com/longhorn/longhorn-engine/pkg/util/fsfreeze.go @@ -0,0 +1,153 @@ +package util + +import ( + "io/fs" + "path/filepath" + "strings" + "time" + + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "k8s.io/mount-utils" + + lhexec "github.com/longhorn/go-common-libs/exec" + "github.com/longhorn/go-common-libs/types" + "github.com/longhorn/go-iscsi-helper/longhorndev" +) + +const ( + binaryFsfreeze = "fsfreeze" + notFrozenErrorSubstring = "Invalid argument" + freezePointDirectory = "/var/lib/longhorn/freeze" // We expect this to be INSIDE the container namespace. + DevicePathPrefix = longhorndev.DevPath + + // If the block device is functioning and the filesystem is frozen, fsfreeze -u immediately returns successfully. + // If the block device is NOT functioning, fsfreeze does not return until I/O errors occur (which can take a long + // time). In certain situations (e.g. when it is executed during an instance-manager shutdown that has already + // stopped the associated replica so that I/Os will eventually time out), waiting can impede the shutdown sequence. + unfreezeTimeout = 5 * time.Second +) + +// GetFreezePointFromDevicePath returns the absolute path to the canonical location we will try to mount a filesystem to +// before freezeing it. +func GetFreezePointFromDevicePath(devicePath string) string { + if devicePath == "" { + return "" + } + return GetFreezePointFromVolumeName(filepath.Base(devicePath)) +} + +// GetFreezePointFromVolumeName returns the absolute path to the canonical location we will try to mount a filesystem to +// before freezeing it. +func GetFreezePointFromVolumeName(volumeName string) string { + if volumeName == "" { + return "" + } + return filepath.Join(freezePointDirectory, volumeName) +} + +// GetDevicePathFromVolumeName mirrors longhorndev.getDev. It returns the device path that go-iscsi-helper will use. +func GetDevicePathFromVolumeName(volumeName string) string { + if volumeName == "" { + return "" + } + return filepath.Join(longhorndev.DevPath, volumeName) +} + +// FreezeFilesystem attempts to freeze the filesystem mounted at freezePoint. +func FreezeFilesystem(freezePoint string, exec lhexec.ExecuteInterface) error { + if exec == nil { + exec = lhexec.NewExecutor() + } + + // fsfreeze cannot be cancelled. Once it is started, we must wait for it to complete. If we do not, unfreeze will + // wait for it anyway. + _, err := exec.Execute([]string{}, binaryFsfreeze, []string{"-f", freezePoint}, types.ExecuteNoTimeout) + if err != nil { + return err + } + return nil +} + +// UnfreezeFilesystem attempts to unfreeze the filesystem mounted at freezePoint. It returns true if it +// successfully unfreezes a filesystem, false if there is no need to unfreeze a filesystem, and an error otherwise. +func UnfreezeFilesystem(freezePoint string, exec lhexec.ExecuteInterface) (bool, error) { + if exec == nil { + exec = lhexec.NewExecutor() + } + + _, err := exec.Execute([]string{}, binaryFsfreeze, []string{"-u", freezePoint}, unfreezeTimeout) + if err == nil { + return true, nil + } + if strings.Contains(err.Error(), notFrozenErrorSubstring) { + return false, nil + } + // It the error message is related to a timeout, there is a decent chance the unfreeze will eventually be + // successful. While we stop waiting for the unfreeze to complete, the unfreeze process itself cannot be killed. + // This usually indicates the kernel is locked up waiting for I/O errors to be returned for an iSCSI device that can + // no longer be reached. + return false, err +} + +// UnfreezeAndUnmountFilesystem attempts to unfreeze the filesystem mounted at freezePoint. +func UnfreezeAndUnmountFilesystem(freezePoint string, exec lhexec.ExecuteInterface, + mounter mount.Interface) (bool, error) { + if exec == nil { + exec = lhexec.NewExecutor() + } + if mounter == nil { + mounter = mount.New("") + } + + unfroze, err := UnfreezeFilesystem(freezePoint, exec) + if err != nil { + return unfroze, err + } + return unfroze, mount.CleanupMountPoint(freezePoint, mounter, false) +} + +// UnfreezeFilesystemForDevice attempts to identify a mountPoint for the Longhorn volume and unfreeze it. Under normal +// conditions, it will not find a filesystem, and if it finds a filesystem, it will not be frozen. +// UnfreezeFilesystemForDevice does not return an error if there is nothing to do. UnfreezeFilesystemForDevice is only +// relevant for volumes run with a tgt-blockdev frontend, as only these volumes have a Longhorn device on the node to +// format and mount. +func UnfreezeFilesystemForDevice(devicePath string) error { + // We do not need to switch to the host mount namespace to get mount points here. Usually, longhorn-engine runs in a + // container that has / bind mounted to /host with at least HostToContainer (rslave) propagation. + // - If it does not, we likely can't do a namespace swap anyway, since we don't have access to /host/proc. + // - If it does, we just need to know where in the container we can access the mount points to unfreeze. + mounter := mount.New("") + freezePoint := GetFreezePointFromDevicePath(devicePath) + + // First, try to unfreeze and unmount the expected mount point. + freezePointIsMountPoint, err := mounter.IsMountPoint(freezePoint) + if err != nil && !errors.Is(err, fs.ErrNotExist) { + logrus.WithError(err).Warnf("Failed to determine if %s is a mount point while deciding whether or not to unfreeze", freezePoint) + } + if freezePointIsMountPoint { + unfroze, err := UnfreezeAndUnmountFilesystem(freezePoint, nil, mounter) + if unfroze { + logrus.Warnf("Unfroze filesystem mounted at %v", freezePoint) + } + return err + } + + // If a filesystem is not mounted at the expected mount point, try any other mount point of the device. + mountPoints, err := mounter.List() + if err != nil { + return errors.Wrap(err, "failed to list mount points while deciding whether or not unfreeze") + } + for _, mountPoint := range mountPoints { + if mountPoint.Device == devicePath { + // This one is not ours to unmount. + unfroze, err := UnfreezeFilesystem(mountPoint.Path, nil) + if unfroze { + logrus.Warnf("Unfroze filesystem mounted at %v", freezePoint) + } + return err + } + } + + return nil +} diff --git a/vendor/github.com/longhorn/longhorn-engine/pkg/util/util.go b/vendor/github.com/longhorn/longhorn-engine/pkg/util/util.go index 7b5d1c73..b97b8de2 100644 --- a/vendor/github.com/longhorn/longhorn-engine/pkg/util/util.go +++ b/vendor/github.com/longhorn/longhorn-engine/pkg/util/util.go @@ -50,25 +50,17 @@ func ParseAddresses(name string) (string, string, string, int, error) { } func GetGRPCAddress(address string) string { - if strings.HasPrefix(address, "tcp://") { - address = strings.TrimPrefix(address, "tcp://") - } + address = strings.TrimPrefix(address, "tcp://") - if strings.HasPrefix(address, "http://") { - address = strings.TrimPrefix(address, "http://") - } + address = strings.TrimPrefix(address, "http://") - if strings.HasSuffix(address, "/v1") { - address = strings.TrimSuffix(address, "/v1") - } + address = strings.TrimSuffix(address, "/v1") return address } func GetPortFromAddress(address string) (int, error) { - if strings.HasSuffix(address, "/v1") { - address = strings.TrimSuffix(address, "/v1") - } + address = strings.TrimSuffix(address, "/v1") _, strPort, err := net.SplitHostPort(address) if err != nil { diff --git a/vendor/github.com/longhorn/longhorn-engine/pkg/util/validation.go b/vendor/github.com/longhorn/longhorn-engine/pkg/util/validation.go index 341d0934..ba44b42f 100644 --- a/vendor/github.com/longhorn/longhorn-engine/pkg/util/validation.go +++ b/vendor/github.com/longhorn/longhorn-engine/pkg/util/validation.go @@ -67,8 +67,6 @@ func IsQualifiedName(value string) []string { const dns1123LabelFmt string = "[a-z0-9]([-a-z0-9]*[a-z0-9])?" -var dns1123LabelRegexp = regexp.MustCompile("^" + dns1123LabelFmt + "$") - const dns1123SubdomainFmt = dns1123LabelFmt + "(\\." + dns1123LabelFmt + ")*" const dns1123SubdomainErrorMsg string = "a DNS-1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character" diff --git a/vendor/github.com/power-devops/perfstat/config.go b/vendor/github.com/power-devops/perfstat/config.go index de7230d2..a6df39c6 100644 --- a/vendor/github.com/power-devops/perfstat/config.go +++ b/vendor/github.com/power-devops/perfstat/config.go @@ -1,3 +1,4 @@ +//go:build aix // +build aix package perfstat diff --git a/vendor/github.com/power-devops/perfstat/cpustat.go b/vendor/github.com/power-devops/perfstat/cpustat.go index 902727fb..10f543fa 100644 --- a/vendor/github.com/power-devops/perfstat/cpustat.go +++ b/vendor/github.com/power-devops/perfstat/cpustat.go @@ -1,3 +1,4 @@ +//go:build aix // +build aix package perfstat @@ -20,6 +21,13 @@ import ( "unsafe" ) +var old_cpu_total_stat *C.perfstat_cpu_total_t + +func init() { + old_cpu_total_stat = (*C.perfstat_cpu_total_t)(C.malloc(C.sizeof_perfstat_cpu_total_t)) + C.perfstat_cpu_total(nil, old_cpu_total_stat, C.sizeof_perfstat_cpu_total_t, 1) +} + func CpuStat() ([]CPU, error) { var cpustat *C.perfstat_cpu_t var cpu C.perfstat_id_t @@ -96,3 +104,35 @@ func CpuUtilStat(intvl time.Duration) (*CPUUtil, error) { u := perfstatcpuutil2cpuutil(cpuutil) return &u, nil } + +func CpuUtilTotalStat() (*CPUUtil, error) { + var cpuutil *C.perfstat_cpu_util_t + var new_cpu_total_stat *C.perfstat_cpu_total_t + var data C.perfstat_rawdata_t + + new_cpu_total_stat = (*C.perfstat_cpu_total_t)(C.malloc(C.sizeof_perfstat_cpu_total_t)) + cpuutil = (*C.perfstat_cpu_util_t)(C.malloc(C.sizeof_perfstat_cpu_util_t)) + defer C.free(unsafe.Pointer(cpuutil)) + + r := C.perfstat_cpu_total(nil, new_cpu_total_stat, C.sizeof_perfstat_cpu_total_t, 1) + if r <= 0 { + C.free(unsafe.Pointer(new_cpu_total_stat)) + return nil, fmt.Errorf("error perfstat_cpu_total()") + } + + data._type = C.UTIL_CPU_TOTAL + data.curstat = unsafe.Pointer(new_cpu_total_stat) + data.prevstat = unsafe.Pointer(old_cpu_total_stat) + data.sizeof_data = C.sizeof_perfstat_cpu_total_t + data.cur_elems = 1 + data.prev_elems = 1 + + r = C.perfstat_cpu_util(&data, cpuutil, C.sizeof_perfstat_cpu_util_t, 1) + C.free(unsafe.Pointer(old_cpu_total_stat)) + old_cpu_total_stat = new_cpu_total_stat + if r <= 0 { + return nil, fmt.Errorf("error perfstat_cpu_util()") + } + u := perfstatcpuutil2cpuutil(cpuutil) + return &u, nil +} diff --git a/vendor/github.com/power-devops/perfstat/diskstat.go b/vendor/github.com/power-devops/perfstat/diskstat.go index fc70dfaa..06763b4b 100644 --- a/vendor/github.com/power-devops/perfstat/diskstat.go +++ b/vendor/github.com/power-devops/perfstat/diskstat.go @@ -1,3 +1,4 @@ +//go:build aix // +build aix package perfstat diff --git a/vendor/github.com/power-devops/perfstat/doc.go b/vendor/github.com/power-devops/perfstat/doc.go index 85eaf3e7..9730a61c 100644 --- a/vendor/github.com/power-devops/perfstat/doc.go +++ b/vendor/github.com/power-devops/perfstat/doc.go @@ -1,3 +1,4 @@ +//go:build !aix // +build !aix // Copyright 2020 Power-Devops.com. All rights reserved. @@ -36,24 +37,24 @@ func DisableLVMStat() {} // CpuStat() returns array of CPU structures with information about // logical CPUs on the system. // IBM documentation: -// * https://www.ibm.com/support/knowledgecenter/ssw_aix_72/performancetools/idprftools_perfstat_int_cpu.html -// * https://www.ibm.com/support/knowledgecenter/en/ssw_aix_72/p_bostechref/perfstat_cpu.html +// - https://www.ibm.com/support/knowledgecenter/ssw_aix_72/performancetools/idprftools_perfstat_int_cpu.html +// - https://www.ibm.com/support/knowledgecenter/en/ssw_aix_72/p_bostechref/perfstat_cpu.html func CpuStat() ([]CPU, error) { return nil, fmt.Errorf("not implemented") } // CpuTotalStat() returns general information about CPUs on the system. // IBM documentation: -// * https://www.ibm.com/support/knowledgecenter/ssw_aix_72/performancetools/idprftools_perfstat_glob_cpu.html -// * https://www.ibm.com/support/knowledgecenter/en/ssw_aix_72/p_bostechref/perfstat_cputot.html +// - https://www.ibm.com/support/knowledgecenter/ssw_aix_72/performancetools/idprftools_perfstat_glob_cpu.html +// - https://www.ibm.com/support/knowledgecenter/en/ssw_aix_72/p_bostechref/perfstat_cputot.html func CpuTotalStat() (*CPUTotal, error) { return nil, fmt.Errorf("not implemented") } // CpuUtilStat() calculates CPU utilization. // IBM documentation: -// * https://www.ibm.com/support/knowledgecenter/ssw_aix_72/performancetools/idprftools_perfstat_cpu_util.html -// * https://www.ibm.com/support/knowledgecenter/en/ssw_aix_72/p_bostechref/perfstat_cpu_util.html +// - https://www.ibm.com/support/knowledgecenter/ssw_aix_72/performancetools/idprftools_perfstat_cpu_util.html +// - https://www.ibm.com/support/knowledgecenter/en/ssw_aix_72/p_bostechref/perfstat_cpu_util.html func CpuUtilStat(intvl time.Duration) (*CPUUtil, error) { return nil, fmt.Errorf("not implemented") } diff --git a/vendor/github.com/power-devops/perfstat/fsstat.go b/vendor/github.com/power-devops/perfstat/fsstat.go index 27f4c06c..d3913197 100644 --- a/vendor/github.com/power-devops/perfstat/fsstat.go +++ b/vendor/github.com/power-devops/perfstat/fsstat.go @@ -1,3 +1,4 @@ +//go:build aix // +build aix package perfstat diff --git a/vendor/github.com/power-devops/perfstat/helpers.go b/vendor/github.com/power-devops/perfstat/helpers.go index e8d69976..d5268ab5 100644 --- a/vendor/github.com/power-devops/perfstat/helpers.go +++ b/vendor/github.com/power-devops/perfstat/helpers.go @@ -1,3 +1,4 @@ +//go:build aix // +build aix package perfstat @@ -7,6 +8,7 @@ package perfstat #include #include +#include #include "c_helpers.h" */ @@ -754,7 +756,7 @@ func fsinfo2filesystem(n *C.struct_fsinfo) FileSystem { i.Device = C.GoString(n.devname) i.MountPoint = C.GoString(n.fsname) i.FSType = int(n.fstype) - i.Flags = int(n.flags) + i.Flags = uint(n.flags) i.TotalBlocks = int64(n.totalblks) i.FreeBlocks = int64(n.freeblks) i.TotalInodes = int64(n.totalinodes) @@ -762,3 +764,56 @@ func fsinfo2filesystem(n *C.struct_fsinfo) FileSystem { return i } + +func lparinfo2partinfo(n C.lpar_info_format2_t) PartitionInfo { + var i PartitionInfo + + i.Version = int(n.version) + i.OnlineMemory = uint64(n.online_memory) + i.TotalDispatchTime = uint64(n.tot_dispatch_time) + i.PoolIdleTime = uint64(n.pool_idle_time) + i.DispatchLatency = uint64(n.dispatch_latency) + i.LparFlags = uint(n.lpar_flags) + i.PCpusInSys = uint(n.pcpus_in_sys) + i.OnlineVCpus = uint(n.online_vcpus) + i.OnlineLCpus = uint(n.online_lcpus) + i.PCpusInPool = uint(n.pcpus_in_pool) + i.UnallocCapacity = uint(n.unalloc_capacity) + i.EntitledCapacity = uint(n.entitled_capacity) + i.VariableWeight = uint(n.variable_weight) + i.UnallocWeight = uint(n.unalloc_weight) + i.MinReqVCpuCapacity = uint(n.min_req_vcpu_capacity) + i.GroupId = uint8(n.group_id) + i.PoolId = uint8(n.pool_id) + i.ShCpusInSys = uint(n.shcpus_in_sys) + i.MaxPoolCapacity = uint(n.max_pool_capacity) + i.EntitledPoolCapacity = uint(n.entitled_pool_capacity) + i.PoolMaxTime = uint64(n.pool_max_time) + i.PoolBusyTime = uint64(n.pool_busy_time) + i.PoolScaledBusyTime = uint64(n.pool_scaled_busy_time) + i.ShCpuTotalTime = uint64(n.shcpu_tot_time) + i.ShCpuBusyTime = uint64(n.shcpu_busy_time) + i.ShCpuScaledBusyTime = uint64(n.shcpu_scaled_busy_time) + i.EntMemCapacity = uint64(n.ent_mem_capacity) + i.PhysMem = uint64(n.phys_mem) + i.VrmPoolPhysMem = uint64(n.vrm_pool_physmem) + i.HypPageSize = uint(n.hyp_pagesize) + i.VrmPoolId = int(n.vrm_pool_id) + i.VrmGroupId = int(n.vrm_group_id) + i.VarMemWeight = int(n.var_mem_weight) + i.UnallocVarMemWeight = int(n.unalloc_var_mem_weight) + i.UnallocEntMemCapacity = uint64(n.unalloc_ent_mem_capacity) + i.TrueOnlineMemory = uint64(n.true_online_memory) + i.AmeOnlineMemory = uint64(n.ame_online_memory) + i.AmeType = uint8(n.ame_type) + i.SpecExecMode = uint8(n.spec_exec_mode) + i.AmeFactor = uint(n.ame_factor) + i.EmPartMajorCode = uint(n.em_part_major_code) + i.EmPartMinorCode = uint(n.em_part_minor_code) + i.BytesCoalesced = uint64(n.bytes_coalesced) + i.BytesCoalescedMemPool = uint64(n.bytes_coalesced_mempool) + i.PurrCoalescing = uint64(n.purr_coalescing) + i.SpurrCoalescing = uint64(n.spurr_coalescing) + + return i +} diff --git a/vendor/github.com/power-devops/perfstat/lparstat.go b/vendor/github.com/power-devops/perfstat/lparstat.go index 0ce35e3c..470a1af2 100644 --- a/vendor/github.com/power-devops/perfstat/lparstat.go +++ b/vendor/github.com/power-devops/perfstat/lparstat.go @@ -1,3 +1,4 @@ +//go:build aix // +build aix package perfstat @@ -6,11 +7,13 @@ package perfstat #cgo LDFLAGS: -lperfstat #include +#include */ import "C" import ( "fmt" + "unsafe" ) func PartitionStat() (*PartitionConfig, error) { @@ -24,3 +27,14 @@ func PartitionStat() (*PartitionConfig, error) { return &p, nil } + +func LparInfo() (*PartitionInfo, error) { + var pinfo C.lpar_info_format2_t + + rc := C.lpar_get_info(C.LPAR_INFO_FORMAT2, unsafe.Pointer(&pinfo), C.sizeof_lpar_info_format2_t) + if rc != 0 { + return nil, fmt.Errorf("lpar_get_info() error") + } + p := lparinfo2partinfo(pinfo) + return &p, nil +} diff --git a/vendor/github.com/power-devops/perfstat/lvmstat.go b/vendor/github.com/power-devops/perfstat/lvmstat.go index eb2064c8..2ce99086 100644 --- a/vendor/github.com/power-devops/perfstat/lvmstat.go +++ b/vendor/github.com/power-devops/perfstat/lvmstat.go @@ -1,3 +1,4 @@ +//go:build aix // +build aix package perfstat diff --git a/vendor/github.com/power-devops/perfstat/memstat.go b/vendor/github.com/power-devops/perfstat/memstat.go index d211a73a..52133f0a 100644 --- a/vendor/github.com/power-devops/perfstat/memstat.go +++ b/vendor/github.com/power-devops/perfstat/memstat.go @@ -1,3 +1,4 @@ +//go:build aix // +build aix package perfstat diff --git a/vendor/github.com/power-devops/perfstat/netstat.go b/vendor/github.com/power-devops/perfstat/netstat.go index 4070da21..847d2946 100644 --- a/vendor/github.com/power-devops/perfstat/netstat.go +++ b/vendor/github.com/power-devops/perfstat/netstat.go @@ -1,3 +1,4 @@ +//go:build aix // +build aix package perfstat diff --git a/vendor/github.com/power-devops/perfstat/procstat.go b/vendor/github.com/power-devops/perfstat/procstat.go index ecafebd8..957ec2b3 100644 --- a/vendor/github.com/power-devops/perfstat/procstat.go +++ b/vendor/github.com/power-devops/perfstat/procstat.go @@ -1,3 +1,4 @@ +//go:build aix // +build aix package perfstat diff --git a/vendor/github.com/power-devops/perfstat/sysconf.go b/vendor/github.com/power-devops/perfstat/sysconf.go index c7454d03..b557da0d 100644 --- a/vendor/github.com/power-devops/perfstat/sysconf.go +++ b/vendor/github.com/power-devops/perfstat/sysconf.go @@ -1,3 +1,4 @@ +//go:build aix // +build aix package perfstat diff --git a/vendor/github.com/power-devops/perfstat/systemcfg.go b/vendor/github.com/power-devops/perfstat/systemcfg.go index 6287eb46..b7c7b725 100644 --- a/vendor/github.com/power-devops/perfstat/systemcfg.go +++ b/vendor/github.com/power-devops/perfstat/systemcfg.go @@ -1,3 +1,4 @@ +//go:build aix // +build aix package perfstat @@ -70,6 +71,7 @@ const ( SC_TM_VER = 59 /* Transaction Memory version, 0 - not capable */ SC_NX_CAP = 60 /* NX GZIP capable */ SC_PKS_STATE = 61 /* Platform KeyStore */ + SC_MMA_VER = 62 ) /* kernel attributes */ @@ -119,6 +121,7 @@ const ( IMPL_POWER7 = 0x8000 /* 7 class CPU */ IMPL_POWER8 = 0x10000 /* 8 class CPU */ IMPL_POWER9 = 0x20000 /* 9 class CPU */ + IMPL_POWER10 = 0x20000 /* 10 class CPU */ ) // Values for implementation field for IA64 Architectures @@ -151,11 +154,13 @@ const ( PV_7 = 0x200000 /* Power PC 7 */ PV_8 = 0x300000 /* Power PC 8 */ PV_9 = 0x400000 /* Power PC 9 */ + PV_10 = 0x500000 /* Power PC 10 */ PV_5_Compat = 0x0F8000 /* Power PC 5 */ PV_6_Compat = 0x108000 /* Power PC 6 */ PV_7_Compat = 0x208000 /* Power PC 7 */ PV_8_Compat = 0x308000 /* Power PC 8 */ PV_9_Compat = 0x408000 /* Power PC 9 */ + PV_10_Compat = 0x508000 /* Power PC 10 */ PV_RESERVED_2 = 0x0A0000 /* source compatability */ PV_RESERVED_3 = 0x0B0000 /* source compatability */ PV_RS2 = 0x040000 /* Power RS2 */ @@ -181,19 +186,21 @@ const ( // Macros for identifying physical processor const ( - PPI4_1 = 0x35 - PPI4_2 = 0x38 - PPI4_3 = 0x39 - PPI4_4 = 0x3C - PPI4_5 = 0x44 - PPI5_1 = 0x3A - PPI5_2 = 0x3B - PPI6_1 = 0x3E - PPI7_1 = 0x3F - PPI7_2 = 0x4A - PPI8_1 = 0x4B - PPI8_2 = 0x4D - PPI9 = 0x4E + PPI4_1 = 0x35 + PPI4_2 = 0x38 + PPI4_3 = 0x39 + PPI4_4 = 0x3C + PPI4_5 = 0x44 + PPI5_1 = 0x3A + PPI5_2 = 0x3B + PPI6_1 = 0x3E + PPI7_1 = 0x3F + PPI7_2 = 0x4A + PPI8_1 = 0x4B + PPI8_2 = 0x4D + PPI9 = 0x4E + PPI9_1 = 0x4E + PPI10_1 = 0x80 ) // Macros for kernel attributes @@ -291,14 +298,32 @@ func GetCPUImplementation() string { return "POWER8" case impl&IMPL_POWER9 != 0: return "POWER9" + case impl&IMPL_POWER10 != 0: + return "Power10" default: return "Unknown" } } +func POWER10OrNewer() bool { + impl := unix.Getsystemcfg(SC_IMPL) + if impl&IMPL_POWER10 != 0 { + return true + } + return false +} + +func POWER10() bool { + impl := unix.Getsystemcfg(SC_IMPL) + if impl&IMPL_POWER10 != 0 { + return true + } + return false +} + func POWER9OrNewer() bool { impl := unix.Getsystemcfg(SC_IMPL) - if impl&IMPL_POWER9 != 0 { + if impl&IMPL_POWER10 != 0 || impl&IMPL_POWER9 != 0 { return true } return false @@ -314,7 +339,7 @@ func POWER9() bool { func POWER8OrNewer() bool { impl := unix.Getsystemcfg(SC_IMPL) - if impl&IMPL_POWER9 != 0 || impl&IMPL_POWER8 != 0 { + if impl&IMPL_POWER10 != 0 || impl&IMPL_POWER9 != 0 || impl&IMPL_POWER8 != 0 { return true } return false @@ -330,7 +355,7 @@ func POWER8() bool { func POWER7OrNewer() bool { impl := unix.Getsystemcfg(SC_IMPL) - if impl&IMPL_POWER9 != 0 || impl&IMPL_POWER8 != 0 || impl&IMPL_POWER7 != 0 { + if impl&IMPL_POWER10 != 0 || impl&IMPL_POWER9 != 0 || impl&IMPL_POWER8 != 0 || impl&IMPL_POWER7 != 0 { return true } return false @@ -419,6 +444,8 @@ func PksEnabled() bool { func CPUMode() string { impl := unix.Getsystemcfg(SC_VERS) switch impl { + case PV_10, PV_10_Compat: + return "Power10" case PV_9, PV_9_Compat: return "POWER9" case PV_8, PV_8_Compat: diff --git a/vendor/github.com/power-devops/perfstat/types_disk.go b/vendor/github.com/power-devops/perfstat/types_disk.go index ca1493d8..50e323db 100644 --- a/vendor/github.com/power-devops/perfstat/types_disk.go +++ b/vendor/github.com/power-devops/perfstat/types_disk.go @@ -29,8 +29,8 @@ type DiskTotal struct { // Disk Adapter Types const ( DA_SCSI = 0 /* 0 ==> SCSI, SAS, other legacy adapter types */ - DA_VSCSI /* 1 ==> Virtual SCSI/SAS Adapter */ - DA_FCA /* 2 ==> Fiber Channel Adapter */ + DA_VSCSI = 1 /* 1 ==> Virtual SCSI/SAS Adapter */ + DA_FCA = 2 /* 2 ==> Fiber Channel Adapter */ ) type DiskAdapter struct { diff --git a/vendor/github.com/power-devops/perfstat/types_fs.go b/vendor/github.com/power-devops/perfstat/types_fs.go index 0be048a3..b4b43ac6 100644 --- a/vendor/github.com/power-devops/perfstat/types_fs.go +++ b/vendor/github.com/power-devops/perfstat/types_fs.go @@ -8,7 +8,7 @@ type FileSystem struct { Device string /* name of the mounted device */ MountPoint string /* where the device is mounted */ FSType int /* File system type, see the constants below */ - Flags int /* Flags of the file system */ + Flags uint /* Flags of the file system */ TotalBlocks int64 /* number of 512 bytes blocks in the filesystem */ FreeBlocks int64 /* number of free 512 bytes block in the filesystem */ TotalInodes int64 /* total number of inodes in the filesystem */ diff --git a/vendor/github.com/power-devops/perfstat/types_lpar.go b/vendor/github.com/power-devops/perfstat/types_lpar.go index 2d3c32fa..f95f8c30 100644 --- a/vendor/github.com/power-devops/perfstat/types_lpar.go +++ b/vendor/github.com/power-devops/perfstat/types_lpar.go @@ -66,3 +66,64 @@ type PartitionConfig struct { TargetMemExpSize int64 /* Expanded Memory Size in MB */ SubProcessorMode int32 /* Split core mode, its value can be 0,1,2 or 4. 0 for unsupported, 1 for capable but not enabled, 2 or 4 for enabled*/ } + +const ( + AME_TYPE_V1 = 0x1 + AME_TYPE_V2 = 0x2 + LPAR_INFO_CAPPED = 0x01 /* Parition Capped */ + LPAR_INFO_AUTH_PIC = 0x02 /* Authority granted for poolidle*/ + LPAR_INFO_SMT_ENABLED = 0x04 /* SMT Enabled */ + LPAR_INFO_WPAR_ACTIVE = 0x08 /* Process Running Within a WPAR */ + LPAR_INFO_EXTENDED = 0x10 /* Extended shared processor pool information */ + LPAR_INFO_AME_ENABLED = 0x20 /* Active Mem. Expansion (AME) enabled*/ + LPAR_INFO_SEM_ENABLED = 0x40 /* Speculative Execution Mode enabled */ +) + +type PartitionInfo struct { + Version int /* version for this structure */ + OnlineMemory uint64 /* MB of currently online memory */ + TotalDispatchTime uint64 /* Total lpar dispatch time in nsecs */ + PoolIdleTime uint64 /* Idle time of shared CPU pool nsecs*/ + DispatchLatency uint64 /* Max latency inbetween dispatches of this LPAR on physCPUS in nsecs */ + LparFlags uint /* LPAR flags */ + PCpusInSys uint /* # of active licensed physical CPUs in system */ + OnlineVCpus uint /* # of current online virtual CPUs */ + OnlineLCpus uint /* # of current online logical CPUs */ + PCpusInPool uint /* # physical CPUs in shared pool */ + UnallocCapacity uint /* Unallocated Capacity available in shared pool */ + EntitledCapacity uint /* Entitled Processor Capacity for this partition */ + VariableWeight uint /* Variable Processor Capacity Weight */ + UnallocWeight uint /* Unallocated Variable Weight available for this partition */ + MinReqVCpuCapacity uint /* OS minimum required virtual processor capacity. */ + GroupId uint8 /* ID of a LPAR group/aggregation */ + PoolId uint8 /* ID of a shared pool */ + ShCpusInSys uint /* # of physical processors allocated for shared processor use */ + MaxPoolCapacity uint /* Maximum processor capacity of partition's pool */ + EntitledPoolCapacity uint /* Entitled processor capacity of partition's pool */ + PoolMaxTime uint64 /* Summation of maximum time that could be consumed by the pool, in nanoseconds */ + PoolBusyTime uint64 /* Summation of busy time accumulated across all partitions in the pool, in nanoseconds */ + PoolScaledBusyTime uint64 /* Scaled summation of busy time accumulated across all partitions in the pool, in nanoseconds */ + ShCpuTotalTime uint64 /* Summation of total time across all physical processors allocated for shared processor use, in nanoseconds */ + ShCpuBusyTime uint64 /* Summation of busy time accumulated across all shared processor partitions, in nanoseconds */ + ShCpuScaledBusyTime uint64 /* Scaled summation of busy time accumulated across all shared processor partitions, in nanoseconds */ + EntMemCapacity uint64 /* Partition's current entitlement memory capacity setting */ + PhysMem uint64 /* Amount of physical memory, in bytes, currently backing the partition's logical memory */ + VrmPoolPhysMem uint64 /* Total amount of physical memory in the VRM pool */ + HypPageSize uint /* Page size hypervisor is using to virtualize partition's memory */ + VrmPoolId int /* ID of VRM pool */ + VrmGroupId int /* eWLM VRM group to which partition belongs */ + VarMemWeight int /* Partition's current variable memory capacity weighting setting */ + UnallocVarMemWeight int /* Amount of unallocated variable memory capacity weight available to LPAR's group */ + UnallocEntMemCapacity uint64 /* Amount of unallocated I/O memory entitlement available to LPAR's group */ + TrueOnlineMemory uint64 /* true MB of currently online memory */ + AmeOnlineMemory uint64 /* AME MB of currently online memory */ + AmeType uint8 + SpecExecMode uint8 /* Speculative Execution Mode */ + AmeFactor uint /* memory expansion factor for LPAR */ + EmPartMajorCode uint /* Major and minor codes for our */ + EmPartMinorCode uint /* current energy management mode */ + BytesCoalesced uint64 /* The number of bytes of the calling partition.s logical real memory coalesced because they contained duplicated data */ + BytesCoalescedMemPool uint64 /* If the calling partition is authorized to see pool wide statistics then the number of bytes of logical real memory coalesced because they contained duplicated data in the calling partition.s memory pool else set to zero.*/ + PurrCoalescing uint64 /* If the calling partition is authorized to see pool wide statistics then PURR cycles consumed to coalesce data else set to zero.*/ + SpurrCoalescing uint64 /* If the calling partition is authorized to see pool wide statistics then SPURR cycles consumed to coalesce data else set to zero.*/ +} diff --git a/vendor/github.com/power-devops/perfstat/uptime.go b/vendor/github.com/power-devops/perfstat/uptime.go index 2bd3e568..86087874 100644 --- a/vendor/github.com/power-devops/perfstat/uptime.go +++ b/vendor/github.com/power-devops/perfstat/uptime.go @@ -1,3 +1,4 @@ +//go:build aix // +build aix package perfstat diff --git a/vendor/modules.txt b/vendor/modules.txt index dbba2970..d91db429 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -115,7 +115,7 @@ github.com/coreos/go-systemd/v22/dbus # github.com/cpuguy83/go-md2man/v2 v2.0.4 ## explicit; go 1.11 github.com/cpuguy83/go-md2man/v2/md2man -# github.com/felixge/httpsnoop v1.0.3 +# github.com/felixge/httpsnoop v1.0.4 ## explicit; go 1.13 github.com/felixge/httpsnoop # github.com/gammazero/deque v0.2.1 @@ -127,7 +127,7 @@ github.com/gammazero/workerpool # github.com/go-logr/logr v1.4.2 ## explicit; go 1.18 github.com/go-logr/logr -# github.com/go-ole/go-ole v1.2.6 +# github.com/go-ole/go-ole v1.3.0 ## explicit; go 1.12 github.com/go-ole/go-ole github.com/go-ole/go-ole/oleutil @@ -183,8 +183,15 @@ github.com/longhorn/go-common-libs/sync github.com/longhorn/go-common-libs/sys github.com/longhorn/go-common-libs/types github.com/longhorn/go-common-libs/utils -# github.com/longhorn/longhorn-engine v1.6.0-dev-20231217.0.20240418025706-519598108463 -## explicit; go 1.21 +# github.com/longhorn/go-iscsi-helper v0.0.0-20240811043302-df8de353dd58 +## explicit; go 1.22.0 +github.com/longhorn/go-iscsi-helper/iscsi +github.com/longhorn/go-iscsi-helper/iscsidev +github.com/longhorn/go-iscsi-helper/longhorndev +github.com/longhorn/go-iscsi-helper/types +github.com/longhorn/go-iscsi-helper/util +# github.com/longhorn/longhorn-engine v1.7.0 +## explicit; go 1.22.2 github.com/longhorn/longhorn-engine/pkg/interceptor github.com/longhorn/longhorn-engine/pkg/replica/client github.com/longhorn/longhorn-engine/pkg/types @@ -231,7 +238,7 @@ github.com/pierrec/lz4/v4/internal/xxh32 # github.com/pkg/errors v0.9.1 ## explicit github.com/pkg/errors -# github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c +# github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 ## explicit; go 1.14 github.com/power-devops/perfstat # github.com/prometheus/client_golang v1.15.0