Giter Site home page Giter Site logo

bmclib's People

Contributors

abdrabo avatar abhay-krishna avatar atrubachev avatar chrisdoherty4 avatar coffeefreak101 avatar diogomatsubara avatar eugeniosiciliano avatar fintelia avatar jacobweinstock avatar joelrebel avatar jwkohnen avatar kwongyhue avatar lero avatar mattcburns avatar mergify[bot] avatar mguezuraga avatar micahhausler avatar ncode avatar nnuss avatar ofaurax avatar omar-m-othman avatar splaspood avatar zevweiss avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

bmclib's Issues

m1000e: check if iDRAC NIC is enabled

The iDRAC NIC can be in a disabled stated (also check screenshot):

# /opt/dell/srvadmin/bin/idracadm7 getniccfg
IPv4 settings:
NIC Enabled          = 0 

Screenshot_2019-03-12_10-51-20

That way, it won't get any IP.

# /opt/dell/srvadmin/bin/idracadm7 setniccfg -o
NIC is now ENABLED

new design proposal

BMCLIB Design Doc

Context and Scope

There are many ways to interact with a BMC. IPMI, Redfish, web API, RACADM, SSH, etc. Not all interaction types are implemented for every vendor. Even if they were, there is no functionality to try multiple interaction types for a request.

The main entrypoint into BMCLIB is discover.ScanAndConnect. This function returns one of two very large interfaces. The user then must type assert or switch on the interface to start using the functionality.

The aim of this new design is to

  • create stronger abstractions through smaller interfaces
  • enhance the success of BMC interactions by trying requests through multiple interaction types
  • allow easier extension of functionality, vendors and interaction types.

Goals and Non-Goals

Goals:

  • Create stronger abstraction through a smaller interfaces
  • Allow provider implementations the ability to grow in implemented functionality without breaking Users
  • Increase the success of BMC requests by trying multiple interaction types
  • Increase simplicity of adding providers

Non-Goals:

  • Change any existing provider implementation details
  • Make API breaking changes

New Design

Overview

Some core tenets of this new design are

  • Small interfaces
  • Implementations are only run if they satisfy the interface requirements
  • Focus on accomplishing a desired BMC interaction not how its done or via what mechanism (ie redfish, ipmitool, web api, etc)
  • All interface functions will try multiple implementations (ipmitool, web api, redfish, ssh, etc) until success or failure by receiving a parameter that is a slice of PowerStateSetter interfaces, shown below.
    func SetPowerState(ctx context.Context, s string, p []PowerStateSetter) (bool, error)

Details

The existing interface methods are all pretty good. The idea here is to split them out into smaller interfaces. This new design can be introduced without breaking existing functionality. Existing functionality would plugin to this and users could choose to use either or both. The ScanAndConnect function would be integrated into the Client box below under the “provider registry helper”. See the code here for working code of this design.
Context Diagram
bmclib-redesign-flow

Code Snippets

User Package

package main
 
import (
   "context"
   "log"
 
   "github.com/bmc-toolbox/bmclib"
)
 
func main() {
   ctx := context.Background()
   client := bmclib.NewClient("127.0.0.1", "ADMIN", "ADMIN")
   err := client.GenerateDefaultRegistry(ctx)
   if err != nil {
       panic(err)
   }
 
   // Get BMC Power State
   state, err := client.GetPowerState(ctx)
   if err != nil {
       panic(err)
   }
   log.Println(state)
 
   // List BMC Users
   users, err := client.ReadUsers(ctx)
   if err != nil {
       panic(err)
   }
   log.Println(users)
}
 
// Output
 
power state: on
users: [
 {
   "Auth": "true",
   "Callin": "true",
   "ID": "2",
   "Link": "false",
   "Name": "ADMIN"
 }
]

Client Package
This package provides a GenerateDefaultRegistry helper that gets/generates provider implementations and puts them into a slice for consumption by a base package executor (see the base example below).

package bmclib
 
import (
   "context"
   "errors"
 
   "github.com/bmc-toolbox/bmclib/bmc"
   "github.com/bmc-toolbox/bmclib/discover"
   "github.com/bmc-toolbox/bmclib/logging"
   "github.com/bmc-toolbox/bmclib/providers/ipmitool"
   "github.com/go-logr/logr"
   "github.com/hashicorp/go-multierror"
)
 
// Client for BMC interactions
type Client struct {
   Auth     Auth
   Logger   logr.Logger
   Registry []interface{}
}
 
// GenerateDefaultRegistry updates the registry with the default provider implementations
func (c *Client) GenerateDefaultRegistry(ctx context.Context) (err error) {
   // try discovering and registering a vendor specifc provider
   vendor, scanErr := discover.ScanAndConnect(c.Auth.Host, c.Auth.User, c.Auth.Pass)
   if scanErr != nil {
       err = multierror.Append(err, scanErr)
   } else {
       c.Registry = append(c.Registry, vendor)
   }
 
   // register generic providers
   c.Registry = append(c.Registry, &ipmitool.Conn{ Host: c.Auth.Host,
       User: c.Auth.User,
       Pass: c.Auth.Pass,
       Log:  c.Logger,
   })
 
   return nil
}
 
// GetPowerState pass through to library function
func (c *Client) GetPowerState(ctx context.Context) (state string, err error) {
   var powerStateSetters []bmc.PowerStateSetter
   for _, elem := range c.Registry {
       switch p := elem.(type) {
       case bmc.PowerStateSetter:
           powerStateSetters = append(powerStateSetters, p)
       default:
       }
   }
   if len(powerStateSetters) == 0 {
       return state, errors.New("no registered providers found")
   }
   return bmc.GetPowerState(ctx, powerStateSetters)
}

Base Package
This package holds the interfaces and executor functions. Notice the executor functions take a slice of the interfaces. This allows us to try the function with multiple implementations.

package bmc
import (
   "context"
   "errors"
 
   "github.com/hashicorp/go-multierror"
)
// PowerStateSetter get power state and set power state
type PowerStateSetter interface {
   PowerState(ctx context.Context) (state string, err error)
   PowerSet(ctx context.Context, state string) (ok bool, err error)
}
// SetPowerState sets the power state for a BMC, trying all interface implementations passed in
func SetPowerState(ctx context.Context, state string, p []PowerStateSetter) (ok bool, err error) {
   for _, elem := range p {
       ok, setErr := elem.PowerSet(ctx, state)
       if setErr != nil {
           err = multierror.Append(err, setErr)
           continue
       }
       if !ok {
           err = multierror.Append(err, errors.New("failed to set power state"))
           continue
       }
       return ok, err
   }
   return ok, multierror.Append(err, errors.New("failed to set power state"))
}
 
// GetPowerState sets the power state for a BMC, trying all interface implementations passed in
func GetPowerState(ctx context.Context, p []PowerStateSetter) (state string, err error) {
   for _, elem := range p {
       state, stateErr := elem.PowerState(ctx)
       if stateErr != nil {
           err = multierror.Append(err, stateErr)
           continue
       }
       return state, err
   }
 
   return state, multierror.Append(err, errors.New("failed to get power state"))
}

Trade-Offs

  • There will be lots of small interfaces. This will be a good amount of upfront work to create them all, along with the corresponding functions that take in the interfaces.
  • There will be a lot of similar code duplicated in the corresponding functions that take in the interfaces. Again, just more upfront work.
  • Code readability changes. Instead of being able to look at a single interface and know methods that are available, one would have to search across interfaces for behavior they want to implement.

References

Existing interface

package devices
 
import (
   "crypto/x509"
 
   "github.com/bmc-toolbox/bmclib/cfgresources"
)
 
// Bmc represents all the required bmc items
type Bmc interface {
   Resources() []string
   User([]*cfgresources.User) error
   Syslog(*cfgresources.Syslog) error
   Ntp(*cfgresources.Ntp) error
   Ldap(*cfgresources.Ldap) error
   LdapGroup([]*cfgresources.LdapGroup, *cfgresources.Ldap) error
   Network(*cfgresources.Network) (bool, error)
   SetLicense(*cfgresources.License) error
   Bios(*cfgresources.Bios) error
   Power(*cfgresources.Power) error
   CurrentHTTPSCert() ([]*x509.Certificate, bool, error)
   GenerateCSR(*cfgresources.HTTPSCertAttributes) ([]byte, error)
   UploadHTTPSCert([]byte, string, []byte, string) (bool, error)
   BiosVersion() (string, error)
   HardwareType() string
   Version() (string, error)
   CPU() (string, int, int, int, error)
   Disks() ([]*Disk, error)
   IsBlade() (bool, error)
   License() (string, string, error)
   Memory() (int, error)
   Model() (string, error)
   Name() (string, error)
   Nics() ([]*Nic, error)
   PowerKw() (float64, error)
   PowerState() (string, error)
   IsOn() (bool, error)
   Serial() (string, error)
   Status() (string, error)
   TempC() (int, error)
   Vendor() string
   Slot() (int, error)
   Screenshot() ([]byte, string, error)
   ServerSnapshot() (interface{}, error)
   ChassisSerial() (string, error)
   CheckCredentials() error
   Close() error
   PowerOn() (bool, error)
   PowerOff() (bool, error)
   PxeOnce() (bool, error)
   PowerCycleBmc() (bool, error)
   PowerCycle() (bool, error)
   UpdateCredentials(string, string)
   UpdateFirmware(string, string) (bool, error)
}

Proposed interfaces

package bmc
 
import (
   "context"
   "errors"
)
 
// PowerStateSetter get power state and set power state
type PowerStateSetter interface {
   PowerState(ctx context.Context) (state string, err error)
   PowerSet(ctx context.Context, state string) (ok bool, err error)
}
 
// UserCreator creates a user on a BMC
type UserCreator interface {
   UserCreate(ctx context.Context, user, pass, role string) (ok bool, err error)
}
 
// UserUpdater updates a user on a BMC
type UserUpdater interface {
   UserUpdate(ctx context.Context, user, pass, role string) (ok bool, err error)
}
 
// UserDeleter deletes a user on a BMC
type UserDeleter interface {
   UserDelete(ctx context.Context, user string) (ok bool, err error)
}
 
// UserReader lists all users on a BMC
type UserReader interface {
   UserRead(ctx context.Context) (users []map[string]string, err error)
}

c7000: add_blade_bmc_admins fails if no blades

WARN[0007] Setup resource returned errors.               Error="\nHPE BladeSystem Onboard Administrator\n(C) Copyright 2006-2018 Hewlett Packard Enterprise Development LP\n\nOA-A45D36FD9CFB [SCRIPT MODE]> HPONCFG all  << end_marker\n\n\nBay 1: The blade is not present.\nBay 2: The blade is not present.\nBay 3: The blade is not present.\nBay 4: The blade is not present.\nBay 5: The blade is not present.\nBay 6: The blade is not present.\nBay 7: The blade is not present.\nBay 8: The blade is not present.\nBay 9: The blade is not present.\nBay 10: The blade is not present.\nBay 11: The blade is not present.\nBay 12: The blade is not present.\nBay 13: The blade is not present.\nBay 14: The blade is not present.\nBay 15: The blade is not present.\nBay 16: The blade is not present.\n\n\n<!-- ======== NO RIBCL RESULTS FOUND ======== -->\n\n\nOA-A45D36FD9CFB [SCRIPT MODE]> \n\nHPONCFG all  << end_marker\n<RIBCL VERSION=\"2.0\">\n<LOGIN USER_LOGIN=\"Administrator\" PASSWORD=\"">\n <USER_INFO MODE=\"write\">\n   <ADD_USER\n     USER_NAME=\"Administrator\"\n     USER_LOGIN=\"Administrator\"\n     PASSWORD=\"">\n     <ADMIN_PRIV value =\"Yes\"/>\n     <REMOTE_CONS_PRIV value =\"Yes\"/>\n     <RESET_SERVER_PRIV value =\"Yes\"/>\n     <VIRTUAL_MEDIA_PRIV value =\"Yes\"/>\n     <CONFIG_ILO_PRIV value=\"Yes\"/>\n   </ADD_USER>\n </USER_INFO>\n </LOGIN>\n</RIBCL>\nend_marker\n" IPAddress=10.194.84.49 Model="BladeSystem c7000 DDR2 Onboard Administrator with KVM" Serial=cz3 Vendor=HP resource=add_blade_bmc_admins
INFO[0011] Chassis setup actions done.                   IPAddress=10.194.84.49 Model="BladeSystem c7000 DDR2 Onboard Administrator with KVM" Serial=cz3 Vendor=HP applied="remove_blade_bmc_users, dynamicpower, bladespower" unsuccessful=

Deleted tags - Cut a v0.5.3 release

There is a mismatch and some apparently deleted git tags between GitHub and Go's module package cache

The latest GitHub release shows up as v0.4.15, while Go's package cache shows v0.5.2.

In order for importers to use the correct latest, can we cut a tag for v0.5.3?

Want support for power diag

Using ipmitool, I can send chassis power diag to issue a non-maskable interrupt (NMI). Operating systems can be configured to take certain actions upon receiving an NMI, which might include initiating a crash dump for post-mortem analysis.

Sending power diag via bmclib should be supported.

idrac8 bmc.Screenshot() returns error

Error returned is requested page couldn't be found in the server

https://github.com/bmc-toolbox/bmclib/blob/master/providers/dell/idrac8/query.go#L13

We're missing a step before the capture, this was missed because I would login to the BMC before hand and a capture was already generated.

The fix would be to,

  1. query https://<ip>/data?get=consolepreview[auto 1540366265264]
  2. then query capconsole/scapture0.png?1540366265265

Sample code here https://gist.github.com/joelrebel/e08b0a1ea02f9be7e041accaa8a57bb7

c7000: Add support to add users to all blade ILOs through chassis

Its possible to add/modify/delete a user on all blade ILOs hooked up in the chassis,
this will make it easier to configure ILOs,

  • If they have unknown passwords.
  • Without hassel of logging into to each ILO to configure it.
OA-3863BB3189E1> HPONCFG all  << end_marker
<RIBCL VERSION="2.0">           
<LOGIN USER_LOGIN="foo" PASSWORD="foobar">   
 <USER_INFO MODE="write">              
   <ADD_USER                                  
     USER_NAME="foo"  
     USER_LOGIN="foo" 
     PASSWORD="foobar">                     
     <ADMIN_PRIV value ="Yes"/>
     <REMOTE_CONS_PRIV value ="Yes"/>
     <RESET_SERVER_PRIV value ="Yes"/>         
     <VIRTUAL_MEDIA_PRIV value ="Yes"/>                          
     <CONFIG_ILO_PRIV value="Yes"/>
   </ADD_USER>        
 </USER_INFO>                                   
 </LOGIN>                                                                      
</RIBCL>                                                                    
end_marker      

Integration with mgmtconfig and event streams

Hiya,

I'm purpleidea, the main developer behind: https://github.com/purpleidea/mgmt/
I just watched this talk from ASG: https://www.youtube.com/watch?v=KrWYb-xO1FE
Thanks for sharing!

I think that this project would integrate nicely with an mgmt resource. https://github.com/purpleidea/mgmt/blob/master/docs/resource-guide.md
The win in doing this, is you'd be able to use mgmt's engine and language to describe your desired state of hardware, and also you'd get more testing of your library.

Secondly, I wanted to suggest that your API add a mechanism to detect events from the BMC (if it doesn't already). My understanding is that this is possible without polling from newer BMC's, and particular OpenBMC ( https://github.com/openbmc/docs/blob/master/rest-api.md#event-subscription-protocol ) however I'm not familiar enough with idrac, ilo, and friends to know what is possible. Mgmt can make use of an event stream if one is available, which is why it would be great to have!

If you'd like to discuss more, please let me know! If there's no interest, feel free to just close this issue, I won't be offended.

On the personal side of things, I've had a hell time dealing with supermicro tooling around their BMC's, so anything that removes part of that from the equation, is a valued tool to me. Hopefully you can push all of your vendors to move to OpenBMC and other similar solutions so we can kill off the proprietary code in all hardware.

Thanks!
James

LGPLv3 dependency

We indirectly consume the bmclib library when we build the Tinkerbell PBNJ binary but one of the transitive dependencies (gebn/bmc) is licensed under LGPLv3.

Output of go mod graph on PBNJ project:

github.com/tinkerbell/pbnj github.com/bmc-toolbox/[email protected]
github.com/bmc-toolbox/[email protected] github.com/gebn/[email protected]

This causes compliance and legal issues and makes it incompatible for our use-case. I believe LGPLv3 also requires that libraries be dynamically linked to applications if the original license of the application is to be preserved (that is, if you statically link against a LGPL library, you must license the entire application code with LGPLv3). Could you consider removing the dependency on the LGPLv3 library, or suggest other options to maintain compatibility with licenses such as Apache-2.0 and MIT License?

idrac9 4.20.20.20 inventory endpoint error

The inventory endpoint that would return XML data now returns a 500 internal error on the R6515 Idrac9's

Endpoint: /sysmgmt/2012/server/inventory/hardware

PowerEdge R6515 
BIOS Version | 1.4.8
iDRAC Firmware Version | 4.20.20.20

Add protocols type as one or more means of BMC access

BMCs can be accessed over one or more of a given set of protocols,

  • HTTPs
  • SSH
  • IPMI
  • Redfish
  • others - GRPC (ubmc)

We add protocols as a type, each BMC can then declare/be probed for the access protocols supported,
this extends bmclib to be able to,

  • Let the caller pick the preferred protocol (if the hardware type/model is known before hand)
  • Let the caller pick the order of protocols attempted
  • IPMI is added as a separate protocol, instead of mixing the concept as a Provider

Add support for BmcCollection via Redfish

We are working on the Redfish support, this tracks the progress of BmcColletion

  • BiosVersion
  • HardwareType
  • Version
  • CPU
  • Disks
  • IsBlade
  • License
  • Memory
  • Model
  • Name
  • Nics
  • PowerKw
  • PowerState
  • IsOn
  • Serial
  • Status
  • TempC
  • Vendor
  • Slot
  • Screenshot
  • ServerSnapshot
  • ChassisSerial

Add support for Bmc via Redfish

We are working on the Redfish support, this tracks the progress of Bmc

  • // Configure
  • // BmcCollection
  • ApplyCfg
  • CheckCredentials
  • Close
  • PowerOn
  • PowerOff
  • PxeOnce
  • PowerCycleBmc
  • PowerCycle
  • UpdateCredentials
  • UpdateFirmware

logout action seems to hang

{"ip":"xxxx","level":"debug","msg":"logout from bmc http","step":"bmc connection","time":"2019-05-29T14:23:54+02:00","vendor":"HP"}

and it just stays there ^^

ipmitool chassis power reset issue

func (i *Ipmi) PowerReset(ctx context.Context) (status bool, err error) {

ipmitool chassis power reset on a powered-off machine returns true but doesn't actually do anything. This was tested on a single supermicro box. I don't know if this is a thing across the board. I couldn't find any good documentation around the expected behavior. Don't know if we just want to be a comment here to make a note of this or if we want to make a definition and then do some status checking before. Something to think about/discuss.

Support for redfish based power/boot actions

Since HP, Dell and Supermicro have decent support for Redfish to carry out power actions,
and these actions seem to work reliably - much better than the IPMI based actions,
we can add this support to bmclib

As a start we add support for these methods,

This could be implemented by integrating with redgopher

  • PXE once
curl -sk -H "$token" -H "Content-Type: application/json" -X PATCH https://$1/redfish/v1/Systems/1/ -d '{"Boot": {"BootSourceOverrideTarget": "Pxe", "BootSourceOverrideEnabled": "Once"}}'
  • Restart
curl -sk -H "$token" -H "Content-Type: application/json" -X POST https://$1/redfish/v1/Systems/1/Actions/ComputerSystem.Reset/ -d '{"ResetType": "ForceRestart"}'
  • Power off
#curl -sk -H "$token" -H "Content-Type: application/json" -X POST https://$1/redfish/v1/Systems/1/Actions/ComputerSystem.Reset/ -d '{"ResetType": "ForceOff"}'
  • Power on
curl -sk -H "$token" -H "Content-Type: application/json" -X POST https://$1/redfish/v1/Systems/1/Actions/ComputerSystem.Reset/ -d '{"ResetType": "On"}'

As a bonus, just for the HP Gen 10 and higher we add

curl -sk -H "$token" -H "Content-Type: application/json" -X PATCH https://$1/redfish/v1/Systems/1/ -d '{"Boot": {"BootSourceOverrideTarget": "UefiHttp", "BootSourceOverrideEnabled": "Once"}}'

Task list

  • HP
  • Dell
  • Supermicro

need new/update ScanAndConnect for new interfaces

with the new interfaces and registry design that we have, it looks like we're going to need an update or create a new ScanAndConnect that can probe a BMC and update the registry with all compatible providers and their functionality. currently client.go has a function place holder for this general idea.

func (c *Client) DiscoverProviders(ctx context.Context) (err error) {

redfish next boot support in new interfaces

With the new interfaces design, adding support for new functionality can be handle in nice small increments. This issue is to implement redfish next boot support. I should be able to tackle this here, shortly.

Add support for network configuration

It would be nice if bmclib could change the network configuration settings such as switching between dhcp and static as well as defining the ip address information when setting to static.

ilo4/5: .Close() does not end the http session correctly.

https://github.com/bmc-toolbox/bmclib/blob/master/providers/hp/ilo/setupConnection.go#L121

DEBU[0006] logout from bmc http ip=10.10.1.1 step="bmc connection" vendor=HP
INFO[0006] [Request] https://10.10.1.1/json/login_session
INFO[0006] >>>>>>>>>>>>>>>
INFO[0006] POST /json/login_session HTTP/1.1
Host: 10.10.1.1
User-Agent: Go-http-client/1.1
Content-Length: 19
Content-Type: application/json
Accept-Encoding: gzip

{"method":"logout"}

INFO[0006] >>>>>>>>>>>>>>>
INFO[0007] [Response]
INFO[0007] <<<<<<<<<<<<<<
INFO[0007] HTTP/1.1 400 Bad Request
Connection: close
Content-Length: 40960
Content-Type: text/plain
Date: Wed, 06 Feb 2019 13:36:30 GMT
X-Frame-Options: sameorigin

Document new interfaces

I think we need some documentation around the new interfaces. doc.go, examples/main.go, README.md are probably good places to start.

concern - `UserDelete` method called on only existing BMC user

With this interface method below, we could potentially lock ourselves out of a BMC by removing all users. If the user given is the only user in the BMC, we might not want to delete it.

UserDelete(ctx context.Context, user string) (ok bool, err error)

We should probably add some comments to the method so that implementors know they should check if the user is the only user left before deleting. We could also maybe add a parameter like force bool to delete the user regardless.

difference in supermicro X11s

got some feedback that the current support for SuperMicro X11 doesn't work for all models of X11. The existing X11 support was written and tested with X11SCM-F in mind. It was reported that X11DSC+ appears to not work, but might work if it was using the X10 code.

I think we need to re-evaluate how we probe for compatibility between SuperMicro models.

Currently, after determining that the BMC is a SuperMicro we call conn.HardwareType() here and here and compare it to a constant of the BMC model number. either x11 or x10. Whichever one it matches we use. This leaves
X11DSC+ not functioning, as reported.

One option could be to not only check this value but if the conn.HardwareType() (or another command) actually worked. and then use that provider implementation.

Any other thoughts or ideas?

Add support for Configure via Redfish

We are working on the Redfish support, this tracks the progress of Configure

  • Resources
  • User
  • Syslog
  • Ntp
  • Ldap
  • LdapGroup
  • Network
  • SetLicense
  • Bios
  • CurrentHTTPSCert
  • GenerateCSR
  • UploadHTTPSCert

ilo5: ntp configuration fails.

This code was written for ilo4 and seems incompatible with ilo5, split code into ilo4, ilo5 ?
https://github.com/bmc-toolbox/bmclib/blob/master/providers/hp/ilo/configure.go#L441

DEBUG_BMCLIB=1 DEBUG_BMCLOGIN=1 /tmp/bmcbutler configure --serials abc123

DEBU[0005] retrieving data from bmc                      endpoint=json/network_sntp/interface/0 ip=10.193.254.148 step="bmc connection" vendor=HP
INFO[0005] [Request] https://10.193.254.148/json/network_sntp/interface/0
INFO[0005] >>>>>>>>>>>>>>>
INFO[0005] GET /json/network_sntp/interface/0 HTTP/1.1
Host: 10.193.254.148
User-Agent: Go-http-client/1.1
Cookie: sessionKey=da3af2a50a731d0f9178c3913bf414a2
Accept-Encoding: gzip

INFO[0005] >>>>>>>>>>>>>>>
INFO[0005] [Response]
INFO[0005] <<<<<<<<<<<<<<
INFO[0005] HTTP/1.1 200 OK
Connection: close
Content-Length: 308
Cache-Control: no-cache
Content-Type: application/x-javascript
Date: Thu, 25 Oct 2018 12:17:13 GMT
X-Frame-Options: sameorigin

{"interface":0,"pending_change":0,"nic_wcount":353,"tz_wcount":2,"ipv4_disabled":0,"ipv6_disabled":0,"dhcp_enabled":1,"dhcp6_enabled":1,"use_dhcp_supplied_time_servers":1,"use_dhcp6_supplied_time_servers":1,"sntp_server1":"","sntp_server2":"","our_zone":14,"sdn1_wcount":1,"sdn2_wcount":1,"time_propagate":0}


INFO[0005] <<<<<<<<<<<<<<
INFO[0005] [Request] 10.193.254.148/json/network_sntp
INFO[0005] >>>>>>>>>>>>>>>
INFO[0005] POST /json/network_sntp HTTP/1.1
Host: 10.193.254.148
User-Agent: Go-http-client/1.1
Content-Length: 420
Cookie: sessionKey=da3af2a50a731d0f9178c3913bf414a2
Accept-Encoding: gzip

{"interface":0,"pending_change":0,"nic_wcount":353,"tz_wcount":2,"ipv4_disabled":0,"ipv6_disabled":0,"dhcp_enabled":1,"dhcp6_enabled":1,"use_dhcp_supplied_time_servers":0,"use_dhcp6_supplied_time_servers":0,"sdn1_wcount":1,"sdn2_wcount":1,"sntp_server1":"ntp0.example.com","sntp_server2":"ntp1.example.com","time_propagate":0,"our_zone":368,"method":"set_sntp","session_key":"da3af2a50a731d0f9178c3913bf414a2"}

INFO[0005] >>>>>>>>>>>>>>>
INFO[0005] [Response]
INFO[0005] <<<<<<<<<<<<<<
INFO[0005] HTTP/1.1 500 Internal Server Error
Connection: close
Content-Length: 63
Content-Type: text/plain
Date: Thu, 25 Oct 2018 12:17:13 GMT
X-Frame-Options: sameorigin

{"message":"JS_ERR_FAILURE","details":"Unable to look up zone"}

INFO[0005] <<<<<<<<<<<<<<
WARN[0005] POST request to set NTP config returned error.  Error="<nil>" IP=10.193.254.148 Model=ilo5 Serial=_SNIP_ StatusCode=500 endpoint=json/network_sntp response="{\"message\":\"JS_ERR_FAILURE\",\"details\":\"Unable to look up zone\"}" step=applyNtpParams
WARN[0005] Unable to set NTP config.                     Error="POST request to set NTP config returned error." IP=10.193.254.148 Model=ilo5 Serial=_SNIP_ resource=ptr step=ApplyCfg

idrac9 PowerEdge R6515 not working

it appears that PowerEdge R6515 uses different endpoints than what is in the existing provider.
the idrac9 provider currently uses sysmgmt/2012/, but the PowerEdge R6515 that I'm testing against uses /sysmgmt/2015/. I don't know if this is specific to the firmware version of the bmc, but the idrac9 provider for this bmc doesn't work. It gets probed and discovered as an idrac9 but no calls actually work.

Add an option to purge unmanaged users/groups

If someone manages to add a user or a group to the BMC, it will be unnoticed forever. This is not ideal from a security point of view. We would like to add an option to purge any user or group that is not specified in the configuration. With this option bmcbutler will remove the unmanaged users/groups on the next run.

I propose to add 2 new bool keys to the configuration (purgeUnmanagedUsers & purgeUnmanagedLdapGroups). This way it will be optional and it will not break backward compatibility.

Add support to configure HTTPS TLS certs

BMC Generates CSR Supports crt/key pair upload
M1000e ✔️ ✔️
iDRAC8 ✔️ ✔️
iDRAC9 ✔️ ✔️
ILO4 ✔️
ILO5 ✔️
C7000 ✔️
Supermicro X10 ✔️

To support these various cases, we add bmclib methods to,

  • List current certificate
  • Generate a CSR
  • Upload a signed certificate
  • Upload a signed certificate + key file

Done:

  • iDRAC8
  • iDRAC9
  • ILO4
  • ILO5
  • Supermicro X10

discover.ScanAndConnect errors if a BMC doesn't have a web endpoint on port 443

discover.ScanAndConnect fails if the BMC host doesn't have a service to connect to on port 443.

Get "https://127.0.0.1/xmldata?item=all": dial tcp 127.0.0.1:443: connect: connection refused

How to reproduce:

# start a ipmi simulator
docker run -d -p 623:623/udp vaporio/ipmi-simulator

# edit examples/main.go with local bmc connection details
sed -i.bak 's/ip := "<bmc_ip>"/ip := "127.0.0.1"/g' examples/main.go
sed -i.bak 's/user := "user"/user := "ADMIN"/g' examples/main.go
sed -i.bak 's/pass := "password"/pass := "ADMIN"/g' examples/main.go
rm -rf example/main.go.bak

# run the main.go example
go run examples/main.go
FATA[0000] Get "https://127.0.0.1/xmldata?item=all": dial tcp 127.0.0.1:443: connect: connection refused 
exit status 1

Expected behavior:

I would expect that if a BMC doesn't server anything on port 443 then, that specific provider would fail out with a errors.ErrDeviceNotMatched or similar but not to fail the discovery process. Something possibly like this:

go run examples/main.go
FATA[0000] unable to identify the vendor 

support changing a user's name with the new interfaces

with the new interfaces in ./bmc, In order to update an existing user's name, the client would need to call a combination of CreateUser and DeleteUser. I propose we add a helper function in user.go to handle this for the client.

I think the code below will cover it. I just need to do and add some tests and then I'll open a PR. Also, trying to get better at creating an issue before a PR :)

// UpdateUserNameFromInterfaces is a helper function that will update a user's name by calling delete and create
func UpdateUserNameFromInterfaces(ctx context.Context, currentUsername, newUsername, pass, role string, generic []interface{}) (ok bool, err error) {
	ok, err = CreateUserFromInterfaces(ctx, newUsername, pass, role, generic)
	if err != nil {
		return false, err
	}
	if !ok {
		return false, fmt.Errorf("updating username was NOT successful: creating user: %v, failed: reason unknown", newUsername)
	}
	ok, err = DeleteUserFromInterfaces(ctx, currentUsername, generic)
	if err != nil {
		ok, err = DeleteUserFromInterfaces(ctx, newUsername, generic)
		if err != nil {
			return false, multierror.Append(err, err)
		}
		if !ok {
			err = multierror.Append(err, fmt.Errorf("updating username was NOT successful: delete newly created user: %v, failed: reason unknown", newUsername))
		}
		return false, err
	}
	if !ok {
		return false, fmt.Errorf("updating username was NOT successful: delete user: %v, failed: reason unknown", currentUsername)
	}
	return ok, nil
}

bmclib interface comparison

About

This ticket is mainly a discussion around the newer and older bmclib interfaces

The newer interface methods are referred to as v2, while the older as v1.

For reference the #196 PR implements both the v1, v2 interfaces for Firmware

Outcome

The idea here is to figure if the following points make sense to work through ,

  1. Reduce the amount of boilerplate required for a v2 interface method to be added under the bmc directory
  2. Could we have device probing - to identify the hardware model, type be part of the v2 interface
  3. Is theres a possibility to have some of the v1 methods available as part of the v2 interface (of if this is not a good idea at all)

What follows is a comparison of the two interfaces...

v2 vs v1

v2 - the newer bmclib interface

Pros

  1. The interfaces are smaller, and don't have to be implemented by all providers
  2. No need to type cast the bmc connection/client returned by the New() method
  3. Protocol fallthrough
  4. Feature declaration
  5. Driver registry
  6. Context propagation

Cons

  1. Driver registry and setup could be simpler
  2. Quite a bit of scafolding/boilerplate code along with test code needs to to be implemented - for example,

BMCVersion method

BMCVersion interface declaration, implementation

BMCVersion tests

  1. Requires a Probing method to identify vendor/model
  • The Compatibility method based on the registrar.Verfier interface could be hacked up to do this although it would be ideal to have one method to probe and identify hardware.

v1 - the older bmclib interface

Pros

  1. A single interface to implement
  2. Low cognitive overhead to add support for new devices/updating
  3. Capable of probing the device to identify vendor/model and returns the appropriate Bmc interface

Cons

  1. All providers need to be updated for a newer interface method or interface method signature change
  2. Lack of context propagation
  3. A new provider needs to implement all Bmc interface methods - even if its going to support just a single method

Configuring admin users on iLO Gen10

At the moment we are using json/user_info endpoint to configure the users on HP servers. Gen10 has a super set of Gen8/9 privileges and seems I can't manage the extra privileges via json/user_info.

GET /json/user_info
{
    "min_password": 8,
    "users": [
        {
            "config_priv": 1,
            "id": 1,
            "login_name": "Administrator",
            "login_priv": 1,
            "remote_cons_priv": 1,
            "reset_priv": 1,
            "user_name": "Administrator",
            "user_priv": 1,
            "virtual_media_priv": 1
        },
...

But we can see on the UI the more privileges (iLO5 2.3/ProLiant BL460c Gen10):
Screenshot 2021-02-09 at 19 09 08

I need the Host BIOS privilege to be able to do some firmware upgrades. I think I will have to reimplement the user configuration for iLO using Redfish as explained in the docs.

FirmwareInstall method could wrap io.Reader for file length

If length is required, maybe consider a different interface like fs.File or you could declare an interface like

type LengthReader interface {  
    io.Reader
    Len() int
}

which is satisfied by bytes.Buffer and strings.Reader and provide a helper for os.File/fs.File like

type lr struct {
  fs.File
}

func (lr *r) Len() int {
    info, err := r.Stat()
    if err != nil {
        panic(err) // or ignore/log
    }
    return int(info.Size())
}

// LengthReaderFromFile returns a LengthReader from an fs.File
func LengthReaderFromFile(f fs.File) LengthReader {
    return &lr{f}
}

Originally posted by @micahhausler in #261 (comment)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.