4 corev1 "k8s.io/api/core/v1"
5 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
6 "k8s.io/apimachinery/pkg/types"
9 // NOTE: json tags are required. Any new fields you add must have
10 // json tags for the fields to be serialized.
12 // NOTE(dhellmann): Update docs/api.md when changing these data structure.
15 // BareMetalHostFinalizer is the name of the finalizer added to
16 // hosts to block delete operations until the physical host can be
18 BareMetalHostFinalizer string = "baremetalhost.metal3.io"
21 // OperationalStatus represents the state of the host
22 type OperationalStatus string
25 // OperationalStatusOK is the status value for when the host is
26 // configured correctly and is manageable.
27 OperationalStatusOK OperationalStatus = "OK"
29 // OperationalStatusDiscovered is the status value for when the
30 // host is only partially configured, such as when when the BMC
31 // address is known but the login credentials are not.
32 OperationalStatusDiscovered OperationalStatus = "discovered"
34 // OperationalStatusError is the status value for when the host
35 // has any sort of error.
36 OperationalStatusError OperationalStatus = "error"
39 // ProvisioningState defines the states the provisioner will report
40 // the host has having.
41 type ProvisioningState string
44 // StateNone means the state is unknown
45 StateNone ProvisioningState = ""
47 // StateRegistrationError means there was an error registering the
48 // host with the backend
49 StateRegistrationError ProvisioningState = "registration error"
51 // StateRegistering means we are telling the backend about the host
52 StateRegistering ProvisioningState = "registering"
54 // StateMatchProfile means we are comparing the discovered details
55 // against known hardware profiles
56 StateMatchProfile ProvisioningState = "match profile"
58 // StateReady means the host can be consumed
59 StateReady ProvisioningState = "ready"
61 // StateValidationError means the provisioning instructions had an
63 StateValidationError ProvisioningState = "validation error"
65 // StateProvisioning means we are writing an image to the host's
67 StateProvisioning ProvisioningState = "provisioning"
69 // StateProvisioningError means we are writing an image to the
71 StateProvisioningError ProvisioningState = "provisioning error"
73 // StateProvisioned means we have written an image to the host's
75 StateProvisioned ProvisioningState = "provisioned"
77 // StateExternallyProvisioned means something else is managing the
79 StateExternallyProvisioned ProvisioningState = "externally provisioned"
81 // StateDeprovisioning means we are removing an image from the
83 StateDeprovisioning ProvisioningState = "deprovisioning"
85 // StateInspecting means we are running the agent on the host to
86 // learn about the hardware components available there
87 StateInspecting ProvisioningState = "inspecting"
89 // StatePowerManagementError means something went wrong trying to
90 // power the server on or off.
91 StatePowerManagementError ProvisioningState = "power management error"
94 // BMCDetails contains the information necessary to communicate with
95 // the bare metal controller module on host.
96 type BMCDetails struct {
98 // Address holds the URL for accessing the controller on the
100 Address string `json:"address"`
102 // The name of the secret containing the BMC credentials (requires
103 // keys "username" and "password").
104 CredentialsName string `json:"credentialsName"`
107 // BareMetalHostSpec defines the desired state of BareMetalHost
108 type BareMetalHostSpec struct {
109 // Important: Run "operator-sdk generate k8s" to regenerate code
110 // after modifying this file
112 // Taints is the full, authoritative list of taints to apply to
113 // the corresponding Machine. This list will overwrite any
114 // modifications made to the Machine on an ongoing basis.
116 Taints []corev1.Taint `json:"taints,omitempty"`
118 // How do we connect to the BMC?
119 BMC BMCDetails `json:"bmc,omitempty"`
121 // What is the name of the hardware profile for this host? It
122 // should only be necessary to set this when inspection cannot
123 // automatically determine the profile.
124 HardwareProfile string `json:"hardwareProfile,omitempty"`
126 // Which MAC address will PXE boot? This is optional for some
127 // types, but required for libvirt VMs driven by vbmc.
128 // +kubebuilder:validation:Pattern=[0-9a-fA-F]{2}(:[0-9a-fA-F]{2}){5}
129 BootMACAddress string `json:"bootMACAddress,omitempty"`
131 // Should the server be online?
132 Online bool `json:"online"`
134 // ConsumerRef can be used to store information about something
135 // that is using a host. When it is not empty, the host is
136 // considered "in use".
137 ConsumerRef *corev1.ObjectReference `json:"consumerRef,omitempty"`
139 // Image holds the details of the image to be provisioned.
140 Image *Image `json:"image,omitempty"`
142 // UserData holds the reference to the Secret containing the user
143 // data to be passed to the host before it boots.
144 UserData *corev1.SecretReference `json:"userData,omitempty"`
146 // Description is a human-entered text used to help identify the host
147 Description string `json:"description,omitempty"`
149 // ExternallyProvisioned means something else is managing the
150 // image running on the host and the operator should only manage
151 // the power status and hardware inventory inspection. If the
152 // Image field is filled in, this field is ignored.
153 ExternallyProvisioned bool `json:"externallyProvisioned,omitempty"`
156 // Image holds the details of an image either to provisioned or that
157 // has been provisioned.
159 // URL is a location of an image to deploy.
160 URL string `json:"url"`
162 // Checksum is the checksum for the image.
163 Checksum string `json:"checksum"`
166 // FIXME(dhellmann): We probably want some other module to own these
169 // ClockSpeed is a clock speed in MHz
170 type ClockSpeed float64
172 // ClockSpeed multipliers
174 MegaHertz ClockSpeed = 1.0
175 GigaHertz = 1000 * MegaHertz
178 // Capacity is a disk size in Bytes
181 // Capacity multipliers
184 KibiByte = Byte * 1024
185 KiloByte = Byte * 1000
186 MebiByte = KibiByte * 1024
187 MegaByte = KiloByte * 1000
188 GibiByte = MebiByte * 1024
189 GigaByte = MegaByte * 1000
190 TebiByte = GibiByte * 1024
191 TeraByte = GigaByte * 1000
194 // CPU describes one processor on the host.
196 Arch string `json:"arch"`
197 Model string `json:"model"`
198 ClockMegahertz ClockSpeed `json:"clockMegahertz"`
199 Flags []string `json:"flags"`
200 Count int `json:"count"`
203 // Storage describes one storage device (disk, SSD, etc.) on the host.
204 type Storage struct {
205 // A name for the disk, e.g. "disk 1 (boot)"
206 Name string `json:"name"`
208 // Whether this disk represents rotational storage
209 Rotational bool `json:"rotational"`
211 // The size of the disk in Bytes
212 SizeBytes Capacity `json:"sizeBytes"`
214 // The name of the vendor of the device
215 Vendor string `json:"vendor,omitempty"`
218 Model string `json:"model,omitempty"`
220 // The serial number of the device
221 SerialNumber string `json:"serialNumber"`
223 // The WWN of the device
224 WWN string `json:"wwn,omitempty"`
226 // The WWN Vendor extension of the device
227 WWNVendorExtension string `json:"wwnVendorExtension,omitempty"`
229 // The WWN with the extension
230 WWNWithExtension string `json:"wwnWithExtension,omitempty"`
232 // The SCSI location of the device
233 HCTL string `json:"hctl,omitempty"`
236 // VLANID is a 12-bit 802.1Q VLAN identifier
239 // VLAN represents the name and ID of a VLAN
241 // +kubebuilder:validation:Minimum=0
242 // +kubebuilder:validation:Maximum=4094
243 ID VLANID `json:"id"`
245 Name string `json:"name,omitempty"`
248 // NIC describes one network interface on the host.
250 // The name of the NIC, e.g. "nic-1"
251 Name string `json:"name"`
253 // The name of the model, e.g. "virt-io"
254 Model string `json:"model"`
256 // The device MAC addr
257 // +kubebuilder:validation:Pattern=[0-9a-fA-F]{2}(:[0-9a-fA-F]{2}){5}
258 MAC string `json:"mac"`
260 // The IP address of the device
261 IP string `json:"ip"`
263 // The speed of the device
264 SpeedGbps int `json:"speedGbps"`
266 // The VLANs available
267 VLANs []VLAN `json:"vlans,omitempty"`
269 // The untagged VLAN ID
270 // +kubebuilder:validation:Minimum=0
271 // +kubebuilder:validation:Maximum=4094
272 VLANID VLANID `json:"vlanId"`
274 // Whether the NIC is PXE Bootable
275 PXE bool `json:"pxe"`
278 // Firmware describes the firmware on the host.
279 type Firmware struct {
280 // The BIOS for this firmware
281 BIOS BIOS `json:"bios"`
284 // BIOS describes the BIOS version on the host.
286 // The release/build date for this BIOS
287 Date string `json:"date"`
289 // The vendor name for this BIOS
290 Vendor string `json:"vendor"`
292 // The version of the BIOS
293 Version string `json:"version"`
296 // HardwareDetails collects all of the information about hardware
297 // discovered on the host.
298 type HardwareDetails struct {
299 SystemVendor HardwareSystemVendor `json:"systemVendor"`
300 Firmware Firmware `json:"firmware"`
301 RAMMebibytes int `json:"ramMebibytes"`
302 NIC []NIC `json:"nics"`
303 Storage []Storage `json:"storage"`
305 Hostname string `json:"hostname"`
308 // HardwareSystemVendor stores details about the whole hardware system.
309 type HardwareSystemVendor struct {
310 Manufacturer string `json:"manufacturer"`
311 ProductName string `json:"productName"`
312 SerialNumber string `json:"serialNumber"`
315 // CredentialsStatus contains the reference and version of the last
316 // set of BMC credentials the controller was able to validate.
317 type CredentialsStatus struct {
318 Reference *corev1.SecretReference `json:"credentials,omitempty"`
319 Version string `json:"credentialsVersion,omitempty"`
322 // Match compares the saved status information with the name and
323 // content of a secret object.
324 func (cs CredentialsStatus) Match(secret corev1.Secret) bool {
326 case cs.Reference == nil:
328 case cs.Reference.Name != secret.ObjectMeta.Name:
330 case cs.Reference.Namespace != secret.ObjectMeta.Namespace:
332 case cs.Version != secret.ObjectMeta.ResourceVersion:
338 // BareMetalHostStatus defines the observed state of BareMetalHost
339 type BareMetalHostStatus struct {
340 // Important: Run "operator-sdk generate k8s" to regenerate code
341 // after modifying this file
343 // OperationalStatus holds the status of the host
344 OperationalStatus OperationalStatus `json:"operationalStatus"`
346 // LastUpdated identifies when this status was last observed.
348 LastUpdated *metav1.Time `json:"lastUpdated,omitempty"`
350 // The name of the profile matching the hardware details.
351 HardwareProfile string `json:"hardwareProfile"`
353 // The hardware discovered to exist on the host.
354 HardwareDetails *HardwareDetails `json:"hardware,omitempty"`
356 // Information tracked by the provisioner.
357 Provisioning ProvisionStatus `json:"provisioning"`
359 // the last credentials we were able to validate as working
360 GoodCredentials CredentialsStatus `json:"goodCredentials,omitempty"`
362 // the last credentials we sent to the provisioning backend
363 TriedCredentials CredentialsStatus `json:"triedCredentials,omitempty"`
365 // the last error message reported by the provisioning subsystem
366 ErrorMessage string `json:"errorMessage"`
368 // indicator for whether or not the host is powered on
369 PoweredOn bool `json:"poweredOn"`
372 // ProvisionStatus holds the state information for a single target.
373 type ProvisionStatus struct {
374 // An indiciator for what the provisioner is doing with the host.
375 State ProvisioningState `json:"state"`
377 // The machine's UUID from the underlying provisioning tool
378 ID string `json:"ID"`
380 // Image holds the details of the last image successfully
381 // provisioned to the host.
382 Image Image `json:"image,omitempty"`
385 // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
387 // BareMetalHost is the Schema for the baremetalhosts API
388 // +k8s:openapi-gen=true
389 // +kubebuilder:resource:shortName=bmh;bmhost
390 // +kubebuilder:subresource:status
391 // +kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.operationalStatus",description="Operational status"
392 // +kubebuilder:printcolumn:name="Provisioning Status",type="string",JSONPath=".status.provisioning.state",description="Provisioning status"
393 // +kubebuilder:printcolumn:name="Consumer",type="string",JSONPath=".spec.consumerRef.name",description="Consumer using this host"
394 // +kubebuilder:printcolumn:name="BMC",type="string",JSONPath=".spec.bmc.address",description="Address of management controller"
395 // +kubebuilder:printcolumn:name="Hardware Profile",type="string",JSONPath=".status.hardwareProfile",description="The type of hardware detected"
396 // +kubebuilder:printcolumn:name="Online",type="string",JSONPath=".spec.online",description="Whether the host is online or not"
397 // +kubebuilder:printcolumn:name="Error",type="string",JSONPath=".status.errorMessage",description="Most recent error"
398 type BareMetalHost struct {
399 metav1.TypeMeta `json:",inline"`
400 metav1.ObjectMeta `json:"metadata,omitempty"`
402 Spec BareMetalHostSpec `json:"spec,omitempty"`
403 Status BareMetalHostStatus `json:"status,omitempty"`
406 // Available returns true if the host is available to be provisioned.
407 func (host *BareMetalHost) Available() bool {
408 if host.Spec.ConsumerRef != nil {
411 if host.GetDeletionTimestamp() != nil {
420 // SetErrorMessage updates the ErrorMessage in the host Status struct
421 // when necessary and returns true when a change is made or false when
422 // no change is made.
423 func (host *BareMetalHost) SetErrorMessage(message string) (dirty bool) {
424 if host.Status.OperationalStatus != OperationalStatusError {
425 host.Status.OperationalStatus = OperationalStatusError
428 if host.Status.ErrorMessage != message {
429 host.Status.ErrorMessage = message
435 // ClearError removes any existing error message.
436 func (host *BareMetalHost) ClearError() (dirty bool) {
437 dirty = host.SetOperationalStatus(OperationalStatusOK)
438 if host.Status.ErrorMessage != "" {
439 host.Status.ErrorMessage = ""
445 // setLabel updates the given label when necessary and returns true
446 // when a change is made or false when no change is made.
447 func (host *BareMetalHost) setLabel(name, value string) bool {
448 if host.Labels == nil {
449 host.Labels = make(map[string]string)
451 if host.Labels[name] != value {
452 host.Labels[name] = value
458 // getLabel returns the value associated with the given label. If
459 // there is no value, an empty string is returned.
460 func (host *BareMetalHost) getLabel(name string) string {
461 if host.Labels == nil {
464 return host.Labels[name]
467 // NeedsHardwareProfile returns true if the profile is not set
468 func (host *BareMetalHost) NeedsHardwareProfile() bool {
469 return host.Status.HardwareProfile == ""
472 // HardwareProfile returns the hardware profile name for the host.
473 func (host *BareMetalHost) HardwareProfile() string {
474 return host.Status.HardwareProfile
477 // SetHardwareProfile updates the hardware profile name and returns
478 // true when a change is made or false when no change is made.
479 func (host *BareMetalHost) SetHardwareProfile(name string) (dirty bool) {
480 if host.Status.HardwareProfile != name {
481 host.Status.HardwareProfile = name
487 // SetOperationalStatus updates the OperationalStatus field and returns
488 // true when a change is made or false when no change is made.
489 func (host *BareMetalHost) SetOperationalStatus(status OperationalStatus) bool {
490 if host.Status.OperationalStatus != status {
491 host.Status.OperationalStatus = status
497 // OperationalStatus returns the contents of the OperationalStatus
499 func (host *BareMetalHost) OperationalStatus() OperationalStatus {
500 return host.Status.OperationalStatus
503 // HasError returns a boolean indicating whether there is an error
505 func (host *BareMetalHost) HasError() bool {
506 return host.Status.ErrorMessage != ""
509 // CredentialsKey returns a NamespacedName suitable for loading the
510 // Secret containing the credentials associated with the host.
511 func (host *BareMetalHost) CredentialsKey() types.NamespacedName {
512 return types.NamespacedName{
513 Name: host.Spec.BMC.CredentialsName,
514 Namespace: host.ObjectMeta.Namespace,
518 // NeedsHardwareInspection looks at the state of the host to determine
519 // if hardware inspection should be run.
520 func (host *BareMetalHost) NeedsHardwareInspection() bool {
521 if host.Spec.ExternallyProvisioned {
522 // Never perform inspection if we already know something is
523 // using the host and we didn't provision it.
526 if host.WasProvisioned() {
527 // Never perform inspection if we have already provisioned
528 // this host, because we don't want to reboot it.
531 return host.Status.HardwareDetails == nil
534 // NeedsProvisioning compares the settings with the provisioning
535 // status and returns true when more work is needed or false
537 func (host *BareMetalHost) NeedsProvisioning() bool {
538 if host.Spec.ExternallyProvisioned {
541 if !host.Spec.Online {
542 // The host is not supposed to be powered on.
545 if host.Spec.Image == nil {
546 // Without an image, there is nothing to provision.
549 if host.Spec.Image.URL == "" {
550 // We have an Image struct but it is empty
553 if host.Status.Provisioning.Image.URL == "" {
554 // We have an image set, but not provisioned.
560 // WasProvisioned returns true when we think we have placed an image
562 func (host *BareMetalHost) WasProvisioned() bool {
563 if host.Spec.ExternallyProvisioned {
566 if host.Status.Provisioning.Image.URL != "" {
567 // We have an image provisioned.
573 // NeedsDeprovisioning compares the settings with the provisioning
574 // status and returns true when the host should be deprovisioned.
575 func (host *BareMetalHost) NeedsDeprovisioning() bool {
576 if host.Spec.ExternallyProvisioned {
579 if host.Status.Provisioning.Image.URL == "" {
582 if host.Spec.Image == nil {
585 if host.Spec.Image.URL != host.Status.Provisioning.Image.URL {
591 // UpdateGoodCredentials modifies the GoodCredentials portion of the
592 // Status struct to record the details of the secret containing
593 // credentials known to work.
594 func (host *BareMetalHost) UpdateGoodCredentials(currentSecret corev1.Secret) {
595 host.Status.GoodCredentials.Version = currentSecret.ObjectMeta.ResourceVersion
596 host.Status.GoodCredentials.Reference = &corev1.SecretReference{
597 Name: currentSecret.ObjectMeta.Name,
598 Namespace: currentSecret.ObjectMeta.Namespace,
602 // UpdateTriedCredentials modifies the TriedCredentials portion of the
603 // Status struct to record the details of the secret containing
604 // credentials known to work.
605 func (host *BareMetalHost) UpdateTriedCredentials(currentSecret corev1.Secret) {
606 host.Status.TriedCredentials.Version = currentSecret.ObjectMeta.ResourceVersion
607 host.Status.TriedCredentials.Reference = &corev1.SecretReference{
608 Name: currentSecret.ObjectMeta.Name,
609 Namespace: currentSecret.ObjectMeta.Namespace,
613 // NewEvent creates a new event associated with the object and ready
614 // to be published to the kubernetes API.
615 func (host *BareMetalHost) NewEvent(reason, message string) corev1.Event {
618 ObjectMeta: metav1.ObjectMeta{
619 GenerateName: reason + "-",
620 Namespace: host.ObjectMeta.Namespace,
622 InvolvedObject: corev1.ObjectReference{
623 Kind: "BareMetalHost",
624 Namespace: host.Namespace,
627 APIVersion: SchemeGroupVersion.Version,
631 Source: corev1.EventSource{
632 Component: "metal3-baremetal-controller",
637 Type: corev1.EventTypeNormal,
638 ReportingController: "metal3.io/baremetal-controller",
639 Related: host.Spec.ConsumerRef,
643 // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
645 // BareMetalHostList contains a list of BareMetalHost
646 type BareMetalHostList struct {
647 metav1.TypeMeta `json:",inline"`
648 metav1.ListMeta `json:"metadata,omitempty"`
649 Items []BareMetalHost `json:"items"`
653 SchemeBuilder.Register(&BareMetalHost{}, &BareMetalHostList{})