bmc-toolbox / bmclib Goto Github PK
View Code? Open in Web Editor NEWLibrary to abstract Baseboard Management Controller interaction
License: Apache License 2.0
Library to abstract Baseboard Management Controller interaction
License: Apache License 2.0
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
Goals:
Non-Goals:
Some core tenets of this new design are
func SetPowerState(ctx context.Context, s string, p []PowerStateSetter) (bool, error)
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
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"))
}
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)
}
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)
}
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=
.. and fix up resulting lint errors
Must include:
power functions
user credential operations
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.
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,
https://<ip>/data?get=consolepreview[auto 1540366265264]
capconsole/scapture0.png?1540366265265
Sample code here https://gist.github.com/joelrebel/e08b0a1ea02f9be7e041accaa8a57bb7
The power states passed as parameters to the PowerSetter
interfaces should be constants and type aliases
https://github.com/bmc-toolbox/bmclib/blob/master/bmc/power.go#L24
This was also suggested in #261 (comment)
type PowerState string
PowerStateOn PowerState = "On"
PowerStateOff PowerState = "Off"
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,
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
bmclib is able to manage DNS configuration for BMCs,
https://github.com/bmc-toolbox/bmclib/blob/master/cfgresources/cfgresources.go#L112
Some BMCs (iLOs) to DDNS register with the configured DNS server,
would be ideal to add support to disable DDNS registration.
The ssh tests are copied all around, it's good enough now to become a module by itself.
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
Need user credential operations for setting passwords.
Seems like I've been testing this method while being logged into the idrac9,
this meant I missed out a request that is required before fetching capconsole/scapture0.png
https://github.com/bmc-toolbox/bmclib/blob/master/providers/dell/idrac9/query.go#L10
To fix this
In some cases x10 will identify themselves as fattin when they aren't and that leads to type identification error. To fix that we need to count the amount of multi-nodes to be sure that it is a fattwin.
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?
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
BMCs can be accessed over one or more of a given set of protocols,
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,
Provider
We are working on the Redfish support, this tracks the progress of BmcColletion
We are working on the Redfish support, this tracks the progress of Bmc
{"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 ^^
Line 73 in 80581fa
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.
I spotted in #245 some cases of Time.Format() like the following:
t.Format("Fri Jun 01")
Unfortunately Fri
and Jun
are not valid format words and end up being passed through as static strings, while 01
has the meaning of "month" in time.Format().
Playground example of the issue and minimal fix : https://go.dev/play/p/50Xota3I8Q1
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
curl -sk -H "$token" -H "Content-Type: application/json" -X PATCH https://$1/redfish/v1/Systems/1/ -d '{"Boot": {"BootSourceOverrideTarget": "Pxe", "BootSourceOverrideEnabled": "Once"}}'
curl -sk -H "$token" -H "Content-Type: application/json" -X POST https://$1/redfish/v1/Systems/1/Actions/ComputerSystem.Reset/ -d '{"ResetType": "ForceRestart"}'
#curl -sk -H "$token" -H "Content-Type: application/json" -X POST https://$1/redfish/v1/Systems/1/Actions/ComputerSystem.Reset/ -d '{"ResetType": "ForceOff"}'
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
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.
Line 67 in 530362e
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.
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.
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
The post code status values returned should be its own types instead of constants to make it easier for caller to compare between the power statuses and post codes.
Current post code constants: https://github.com/bmc-toolbox/bmclib/blob/v2/constants/constants.go#L66
I think we need some documentation around the new interfaces. doc.go
, examples/main.go
, README.md
are probably good places to start.
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.
Line 23 in 85f37d5
force bool
to delete the user regardless.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?
Is it feasible to add support for iAMT (used to manage kiosk like devices)? Or is that out-of-scope?
There is at least the the MeshCentral application/library (written in node.js) that seems to have good support for it.
We are working on the Redfish support, this tracks the progress of Configure
After understanding internal packages in Go, I now know why we have to have such a code in Actor. My question is: Why?
The code in Actor is now inconsistent, and it will definitely remain so.
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
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.
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.
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,
Done:
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
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
}
This is possible using racadm deploy -a -u root -p <password>
First word is best the function name for godoc
: https://go.dev/blog/godoc
Originally posted by @nnuss in #245 (comment)
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
The idea here is to figure if the following points make sense to work through ,
bmc
directoryWhat follows is a comparison of the two interfaces...
Pros
Cons
BMCVersion interface declaration, implementation
registrar.Verfier
interface could be hacked up to do this although it would be ideal to have one method to probe and identify hardware.Pros
Cons
Bmc
interface methods - even if its going to support just a single methodwhile using client.go
it's not known which provider successfully executed a call. It would be nice to have the ability to know which provider successfully executed a call.
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):
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.
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)
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.