...

Source file src/github.com/LINBIT/golinstor/client/controller.go

Documentation: github.com/LINBIT/golinstor/client

     1  // Copyright (C) LINBIT HA-Solutions GmbH
     2  // All Rights Reserved.
     3  // Author: Roland Kammerer <roland.kammerer@linbit.com>
     4  //
     5  // Licensed under the Apache License, Version 2.0 (the "License"); you may
     6  // not use this file except in compliance with the License. You may obtain
     7  // a copy of the License at
     8  //
     9  // http://www.apache.org/licenses/LICENSE-2.0
    10  //
    11  // Unless required by applicable law or agreed to in writing, software
    12  // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
    13  // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
    14  // License for the specific language governing permissions and limitations
    15  // under the License.
    16  
    17  package client
    18  
    19  import (
    20  	"context"
    21  	"encoding/base64"
    22  	"encoding/json"
    23  	"fmt"
    24  	"net/url"
    25  	"strconv"
    26  	"time"
    27  )
    28  
    29  // copy & paste from generated code
    30  
    31  // ControllerVersion represents version information of the LINSTOR controller
    32  type ControllerVersion struct {
    33  	Version        string `json:"version,omitempty"`
    34  	GitHash        string `json:"git_hash,omitempty"`
    35  	BuildTime      string `json:"build_time,omitempty"`
    36  	RestApiVersion string `json:"rest_api_version,omitempty"`
    37  }
    38  
    39  // ErrorReport struct for ErrorReport
    40  type ErrorReport struct {
    41  	NodeName  string      `json:"node_name,omitempty"`
    42  	ErrorTime TimeStampMs `json:"error_time"`
    43  	// Filename of the error report on the server.  Format is: ```ErrorReport-{instanceid}-{nodeid}-{sequencenumber}.log```
    44  	Filename string `json:"filename,omitempty"`
    45  	// Contains the full text of the error report file.
    46  	Text string `json:"text,omitempty"`
    47  	// Which module this error occurred.
    48  	Module string `json:"module,omitempty"`
    49  	// Linstor version this error report was created.
    50  	Version string `json:"version,omitempty"`
    51  	// Peer client that was involved.
    52  	Peer string `json:"peer,omitempty"`
    53  	// Exception that occurred
    54  	Exception string `json:"exception,omitempty"`
    55  	// Exception message
    56  	ExceptionMessage string `json:"exception_message,omitempty"`
    57  	// Origin file of the exception
    58  	OriginFile string `json:"origin_file,omitempty"`
    59  	// Origin method where the exception occurred
    60  	OriginMethod string `json:"origin_method,omitempty"`
    61  	// Origin line number
    62  	OriginLine int32 `json:"origin_line,omitempty"`
    63  }
    64  
    65  type ErrorReportDelete struct {
    66  	Since *TimeStampMs `json:"since,omitempty"`
    67  	To    *TimeStampMs `json:"to,omitempty"`
    68  	// on which nodes to delete error-reports, if empty/null all nodes
    69  	Nodes []string `json:"nodes,omitempty"`
    70  	// delete all error reports with the given exception
    71  	Exception string `json:"exception,omitempty"`
    72  	// delete all error reports from the given version
    73  	Version string `json:"version,omitempty"`
    74  	// error report ids to delete
    75  	Ids []string `json:"ids,omitempty"`
    76  }
    77  
    78  type PropsInfo struct {
    79  	Info     string `json:"info,omitempty"`
    80  	PropType string `json:"prop_type,omitempty"`
    81  	Value    string `json:"value,omitempty"`
    82  	Dflt     string `json:"dflt,omitempty"`
    83  	Unit     string `json:"unit,omitempty"`
    84  }
    85  
    86  // ExternalFile is an external file which can be configured to be deployed by Linstor
    87  type ExternalFile struct {
    88  	Path    string
    89  	Content []byte
    90  }
    91  
    92  // externalFileBase64 is a golinstor-internal type which represents an external
    93  // file as it is handled by the LINSTOR API. The API expects files to come in
    94  // base64 encoding, and also returns files in base64 encoding. To make golinstor
    95  // easier to use, we only present the ExternalFile type to our users an
    96  // transparently handle the base64 encoding/decoding.
    97  type externalFileBase64 struct {
    98  	Path          string `json:"path,omitempty"`
    99  	ContentBase64 string `json:"content,omitempty"`
   100  }
   101  
   102  func (e *ExternalFile) UnmarshalJSON(text []byte) error {
   103  	v := externalFileBase64{}
   104  	err := json.Unmarshal(text, &v)
   105  	if err != nil {
   106  		return err
   107  	}
   108  	e.Path = v.Path
   109  	e.Content, err = base64.StdEncoding.DecodeString(v.ContentBase64)
   110  	if err != nil {
   111  		return err
   112  	}
   113  	return nil
   114  }
   115  
   116  func (e *ExternalFile) MarshalJSON() ([]byte, error) {
   117  	v := externalFileBase64{
   118  		Path:          e.Path,
   119  		ContentBase64: base64.StdEncoding.EncodeToString(e.Content),
   120  	}
   121  	return json.Marshal(v)
   122  }
   123  
   124  // custom code
   125  
   126  // ControllerProvider acts as an abstraction for a ControllerService. It can be
   127  // swapped out for another ControllerService implementation, for example for
   128  // testing.
   129  type ControllerProvider interface {
   130  	// GetVersion queries version information for the controller.
   131  	GetVersion(ctx context.Context, opts ...*ListOpts) (ControllerVersion, error)
   132  	// GetConfig queries the configuration of a controller
   133  	GetConfig(ctx context.Context, opts ...*ListOpts) (ControllerConfig, error)
   134  	// Modify modifies the controller node and sets/deletes the given properties.
   135  	Modify(ctx context.Context, props GenericPropsModify) error
   136  	// GetProps gets all properties of a controller
   137  	GetProps(ctx context.Context, opts ...*ListOpts) (ControllerProps, error)
   138  	// DeleteProp deletes the given property/key from the controller object.
   139  	DeleteProp(ctx context.Context, prop string) error
   140  	// GetErrorReports returns all error reports. The Text field is not populated,
   141  	// use GetErrorReport to get the text of an error report.
   142  	GetErrorReports(ctx context.Context, opts ...*ListOpts) ([]ErrorReport, error)
   143  	// DeleteErrorReports deletes error reports as specified by the ErrorReportDelete struct.
   144  	DeleteErrorReports(ctx context.Context, del ErrorReportDelete) error
   145  	// GetErrorReportsSince returns all error reports created after a certain point in time.
   146  	GetErrorReportsSince(ctx context.Context, since time.Time, opts ...*ListOpts) ([]ErrorReport, error)
   147  	// GetErrorReport returns a specific error report, including its text.
   148  	GetErrorReport(ctx context.Context, id string, opts ...*ListOpts) (ErrorReport, error)
   149  	// CreateSOSReport creates an SOS report in the log directory of the controller
   150  	CreateSOSReport(ctx context.Context, opts ...*ListOpts) error
   151  	// DownloadSOSReport request sos report to download
   152  	DownloadSOSReport(ctx context.Context, opts ...*ListOpts) error
   153  	GetSatelliteConfig(ctx context.Context, node string) (SatelliteConfig, error)
   154  	ModifySatelliteConfig(ctx context.Context, node string, cfg SatelliteConfig) error
   155  	// GetPropsInfos gets meta information about the properties that can be
   156  	// set on a controller.
   157  	GetPropsInfos(ctx context.Context, opts ...*ListOpts) ([]PropsInfo, error)
   158  	// GetPropsInfosAll gets meta information about all properties that can
   159  	// be set on a controller and all entities it contains (nodes, resource
   160  	// definitions, ...).
   161  	GetPropsInfosAll(ctx context.Context, opts ...*ListOpts) ([]PropsInfo, error)
   162  	// GetExternalFiles gets a list of previously registered external files.
   163  	GetExternalFiles(ctx context.Context, opts ...*ListOpts) ([]ExternalFile, error)
   164  	// GetExternalFile gets the requested external file including its content
   165  	GetExternalFile(ctx context.Context, name string) (ExternalFile, error)
   166  	// ModifyExternalFile registers or modifies a previously registered
   167  	// external file
   168  	ModifyExternalFile(ctx context.Context, name string, file ExternalFile) error
   169  	// DeleteExternalFile deletes the given external file. This effectively
   170  	// also deletes the file on all satellites
   171  	DeleteExternalFile(ctx context.Context, name string) error
   172  }
   173  
   174  // ControllerService is the service that deals with the LINSTOR controller.
   175  type ControllerService struct {
   176  	client *Client
   177  }
   178  
   179  // GetVersion queries version information for the controller.
   180  func (s *ControllerService) GetVersion(ctx context.Context, opts ...*ListOpts) (ControllerVersion, error) {
   181  	var vers ControllerVersion
   182  	_, err := s.client.doGET(ctx, "/v1/controller/version", &vers, opts...)
   183  	return vers, err
   184  }
   185  
   186  // GetConfig queries the configuration of a controller
   187  func (s *ControllerService) GetConfig(ctx context.Context, opts ...*ListOpts) (ControllerConfig, error) {
   188  	var cfg ControllerConfig
   189  	_, err := s.client.doGET(ctx, "/v1/controller/config", &cfg, opts...)
   190  	return cfg, err
   191  }
   192  
   193  // Modify modifies the controller node and sets/deletes the given properties.
   194  func (s *ControllerService) Modify(ctx context.Context, props GenericPropsModify) error {
   195  	_, err := s.client.doPOST(ctx, "/v1/controller/properties", props)
   196  	return err
   197  }
   198  
   199  type ControllerProps map[string]string
   200  
   201  // GetProps gets all properties of a controller
   202  func (s *ControllerService) GetProps(ctx context.Context, opts ...*ListOpts) (ControllerProps, error) {
   203  	var props ControllerProps
   204  	_, err := s.client.doGET(ctx, "/v1/controller/properties", &props, opts...)
   205  	return props, err
   206  }
   207  
   208  // DeleteProp deletes the given property/key from the controller object.
   209  func (s *ControllerService) DeleteProp(ctx context.Context, prop string) error {
   210  	_, err := s.client.doDELETE(ctx, "/v1/controller/properties/"+prop, nil)
   211  	return err
   212  }
   213  
   214  // GetPropsInfos gets meta information about the properties that can be set on
   215  // a controller.
   216  func (s *ControllerService) GetPropsInfos(ctx context.Context, opts ...*ListOpts) ([]PropsInfo, error) {
   217  	var infos []PropsInfo
   218  	_, err := s.client.doGET(ctx, "/v1/controller/properties/info", &infos, opts...)
   219  	return infos, err
   220  }
   221  
   222  // GetPropsInfosAll gets meta information about all properties that can be set
   223  // on a controller and all entities it contains (nodes, resource definitions, ...).
   224  func (s *ControllerService) GetPropsInfosAll(ctx context.Context, opts ...*ListOpts) ([]PropsInfo, error) {
   225  	var infos []PropsInfo
   226  	_, err := s.client.doGET(ctx, "/v1/controller/properties/info/all", &infos, opts...)
   227  	return infos, err
   228  }
   229  
   230  // GetErrorReports returns all error reports. The Text field is not populated,
   231  // use GetErrorReport to get the text of an error report.
   232  func (s *ControllerService) GetErrorReports(ctx context.Context, opts ...*ListOpts) ([]ErrorReport, error) {
   233  	var reports []ErrorReport
   234  	_, err := s.client.doGET(ctx, "/v1/error-reports", &reports, opts...)
   235  	return reports, err
   236  }
   237  
   238  // DeleteErrorReports deletes error reports as specified by the ErrorReportDelete struct.
   239  func (s *ControllerService) DeleteErrorReports(ctx context.Context, del ErrorReportDelete) error {
   240  	// Yes, this is using PATCH. don't ask me why, its just implemented that way...
   241  	_, err := s.client.doPATCH(ctx, "/v1/error-reports", del)
   242  	return err
   243  }
   244  
   245  // unixMilli returns t formatted as milliseconds since Unix epoch
   246  func unixMilli(t time.Time) int64 {
   247  	return t.UnixNano() / (int64(time.Millisecond) / int64(time.Nanosecond))
   248  }
   249  
   250  // GetErrorReportsSince returns all error reports created after a certain point in time.
   251  func (s *ControllerService) GetErrorReportsSince(ctx context.Context, since time.Time, opts ...*ListOpts) ([]ErrorReport, error) {
   252  	var reports []ErrorReport
   253  	v := url.Values{}
   254  	v.Set("since", strconv.FormatInt(unixMilli(since), 10))
   255  	_, err := s.client.doGET(ctx, "/v1/error-reports/?"+v.Encode(), &reports, opts...)
   256  	return reports, err
   257  }
   258  
   259  // GetErrorReport returns a specific error report, including its text.
   260  func (s *ControllerService) GetErrorReport(ctx context.Context, id string, opts ...*ListOpts) (ErrorReport, error) {
   261  	var report []ErrorReport
   262  	_, err := s.client.doGET(ctx, "/v1/error-reports/"+id, &report, opts...)
   263  	return report[0], err
   264  }
   265  
   266  // CreateSOSReport creates an SOS report in the log directory of the controller
   267  func (s *ControllerService) CreateSOSReport(ctx context.Context, opts ...*ListOpts) error {
   268  	_, err := s.client.doGET(ctx, "/v1/sos-report", nil, opts...)
   269  	return err
   270  }
   271  
   272  // DownloadSOSReport request sos report to download
   273  func (s *ControllerService) DownloadSOSReport(ctx context.Context, opts ...*ListOpts) error {
   274  	_, err := s.client.doGET(ctx, "/v1/sos-report/download", nil, opts...)
   275  	return err
   276  }
   277  
   278  func (s *ControllerService) GetSatelliteConfig(ctx context.Context, node string) (SatelliteConfig, error) {
   279  	var cfg SatelliteConfig
   280  	_, err := s.client.doGET(ctx, "/v1/nodes/"+node+"/config", &cfg)
   281  	return cfg, err
   282  }
   283  
   284  func (s *ControllerService) ModifySatelliteConfig(ctx context.Context, node string, cfg SatelliteConfig) error {
   285  	_, err := s.client.doPUT(ctx, "/v1/nodes/"+node+"/config", &cfg)
   286  	return err
   287  }
   288  
   289  // GetExternalFiles get a list of previously registered external files.
   290  // File contents are not included, unless ListOpts.Content is true.
   291  func (s *ControllerService) GetExternalFiles(ctx context.Context, opts ...*ListOpts) ([]ExternalFile, error) {
   292  	var files []ExternalFile
   293  	_, err := s.client.doGET(ctx, "/v1/files", &files, opts...)
   294  	return files, err
   295  }
   296  
   297  // GetExternalFile gets the requested external file including its content
   298  func (s *ControllerService) GetExternalFile(ctx context.Context, name string) (ExternalFile, error) {
   299  	file := ExternalFile{}
   300  	_, err := s.client.doGET(ctx, "/v1/files/"+url.QueryEscape(name), &file)
   301  	if err != nil {
   302  		return ExternalFile{}, fmt.Errorf("request failed: %w", err)
   303  	}
   304  	return file, nil
   305  }
   306  
   307  // ModifyExternalFile registers or modifies a previously registered external
   308  // file
   309  func (s *ControllerService) ModifyExternalFile(ctx context.Context, name string, file ExternalFile) error {
   310  	b64file := externalFileBase64{
   311  		Path:          file.Path,
   312  		ContentBase64: base64.StdEncoding.EncodeToString(file.Content),
   313  	}
   314  	_, err := s.client.doPUT(ctx, "/v1/files/"+url.QueryEscape(name), b64file)
   315  	return err
   316  }
   317  
   318  // DeleteExternalFile deletes the given external file. This effectively also
   319  // deletes the file on all satellites
   320  func (s *ControllerService) DeleteExternalFile(ctx context.Context, name string) error {
   321  	_, err := s.client.doDELETE(ctx, "/v1/files/"+url.QueryEscape(name), nil)
   322  	return err
   323  }
   324  

View as plain text