diff --git a/nsxt/data_source_nsxt_upgrade_prepare_ready.go b/nsxt/data_source_nsxt_upgrade_prepare_ready.go new file mode 100644 index 000000000..795fdadbc --- /dev/null +++ b/nsxt/data_source_nsxt_upgrade_prepare_ready.go @@ -0,0 +1,62 @@ +/* Copyright © 2024 VMware, Inc. All Rights Reserved. + SPDX-License-Identifier: MPL-2.0 */ + +package nsxt + +import ( + "fmt" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + nsxModel "github.com/vmware/vsphere-automation-sdk-go/services/nsxt-mp/nsx/model" +) + +func dataSourceNsxtUpgradePrepareReady() *schema.Resource { + return &schema.Resource{ + Read: dataSourceNsxtUpgradePrepareReadyRead, + + Schema: map[string]*schema.Schema{ + "id": getDataSourceIDSchema(), + "upgrade_prepare_id": { + Type: schema.TypeString, + Description: "ID of corresponding nsxt_upgrade_prepare resource", + Required: true, + }, + }, + } +} + +func dataSourceNsxtUpgradePrepareReadyRead(d *schema.ResourceData, m interface{}) error { + precheckErrors, err := getPrecheckErrors(m, nil) + if err != nil { + return fmt.Errorf("Error while reading precheck failures: %v", err) + } + var precheckFailureIDs []string + var unacknowledgedWarningIDs []string + for _, precheckError := range precheckErrors { + errType := *precheckError.Type_ + if errType == nsxModel.UpgradeCheckFailure_TYPE_FAILURE { + precheckFailureIDs = append(precheckFailureIDs, *precheckError.Id) + } else { + if !(*precheckError.Acked) { + unacknowledgedWarningIDs = append(unacknowledgedWarningIDs, *precheckError.Id) + } + } + } + var errMessage string + if len(precheckFailureIDs) > 0 { + errMessage += fmt.Sprintf("There are failures in precheck: %s, please check their status from nsxt_upgrade_prepare resource and address these failures on NSX", precheckFailureIDs) + } + if len(unacknowledgedWarningIDs) > 0 { + errMessage += fmt.Sprintf("\nThere are unacknowledged warnings in precheck: %s, please address these errors from NSX or using nsxt_upgrade_precheck_acknowledge resource", unacknowledgedWarningIDs) + } + if len(errMessage) > 0 { + return fmt.Errorf(errMessage) + } + objID := d.Get("id").(string) + if objID == "" { + objID = newUUID() + } + d.SetId(objID) + + return nil +} diff --git a/nsxt/provider.go b/nsxt/provider.go index 87d2feef4..a7a141a13 100644 --- a/nsxt/provider.go +++ b/nsxt/provider.go @@ -318,6 +318,7 @@ func Provider() *schema.Provider { "nsxt_host_upgrade_group": dataSourceNsxtHostUpgradeGroup(), "nsxt_policy_gateway_interface_realization_info": dataSourceNsxtPolicyGatewayInterfaceRealizationInfo(), "nsxt_upgrade_postcheck": dataSourceNsxtUpgradePostCheck(), + "nsxt_upgrade_prepare_ready": dataSourceNsxtUpgradePrepareReady(), }, ResourcesMap: map[string]*schema.Resource{ @@ -473,6 +474,8 @@ func Provider() *schema.Provider { "nsxt_policy_lb_udp_monitor_profile": resourceNsxtPolicyLBUdpMonitorProfile(), "nsxt_policy_tier0_gateway_gre_tunnel": resourceNsxtPolicyTier0GatewayGRETunnel(), "nsxt_upgrade_run": resourceNsxtUpgradeRun(), + "nsxt_upgrade_prepare": resourceNsxtUpgradePrepare(), + "nsxt_upgrade_precheck_acknowledge": resourceNsxtUpgradePrecheckAcknowledge(), }, ConfigureFunc: providerConfigure, diff --git a/nsxt/resource_nsxt_upgrade_precheck_acknowledge.go b/nsxt/resource_nsxt_upgrade_precheck_acknowledge.go new file mode 100644 index 000000000..2c1c99289 --- /dev/null +++ b/nsxt/resource_nsxt_upgrade_precheck_acknowledge.go @@ -0,0 +1,139 @@ +/* Copyright © 2023 VMware, Inc. All Rights Reserved. + SPDX-License-Identifier: MPL-2.0 */ + +package nsxt + +import ( + "fmt" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + nsxModel "github.com/vmware/vsphere-automation-sdk-go/services/nsxt-mp/nsx/model" + "github.com/vmware/vsphere-automation-sdk-go/services/nsxt-mp/nsx/upgrade" +) + +func resourceNsxtUpgradePrecheckAcknowledge() *schema.Resource { + return &schema.Resource{ + Create: resourceNsxtUpgradePrecheckAcknowledgeCreate, + Read: resourceNsxtUpgradePrecheckAcknowledgeRead, + Update: resourceNsxtUpgradePrecheckAcknowledgeUpdate, + Delete: resourceNsxtUpgradePrecheckAcknowledgeDelete, + + Schema: map[string]*schema.Schema{ + "precheck_ids": { + Type: schema.TypeList, + Description: "IDs of precheck warnings that need to be acknowledged", + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Required: true, + }, + "precheck_warnings": { + Type: schema.TypeList, + Description: "List of warnings from precheck", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Description: "ID of the precheck warning", + Computed: true, + }, + "message": { + Type: schema.TypeString, + Description: "Message of the precheck warning", + Computed: true, + }, + "is_acknowledged": { + Type: schema.TypeBool, + Description: "Boolean value which identifies if the precheck warning has been acknowledged.", + Computed: true, + }, + }, + }, + Computed: true, + }, + }, + } +} + +func resourceNsxtUpgradePrecheckAcknowledgeCreate(d *schema.ResourceData, m interface{}) error { + id := d.Id() + if id == "" { + id = newUUID() + } + d.SetId(id) + precheckIDs := interface2StringList(d.Get("precheck_ids").([]interface{})) + err := acknowledgePrecheckWarnings(m, precheckIDs) + if err != nil { + return handleCreateError("NsxtPrecheckAcknowledge", id, err) + } + return resourceNsxtUpgradePrecheckAcknowledgeRead(d, m) +} + +func acknowledgePrecheckWarnings(m interface{}, precheckIDs []string) error { + connector := getPolicyConnector(m) + client := upgrade.NewPreUpgradeChecksClient(connector) + for _, precheckID := range precheckIDs { + err := client.Acknowledge(precheckID) + if err != nil { + msg := fmt.Sprintf("Failed to acknowledge precheck warning with ID %s", precheckID) + return logAPIError(msg, err) + } + } + return nil +} + +func resourceNsxtUpgradePrecheckAcknowledgeRead(d *schema.ResourceData, m interface{}) error { + id := d.Id() + typeParam := nsxModel.UpgradeCheckFailure_TYPE_WARNING + precheckWarnings, err := getPrecheckErrors(m, &typeParam) + if err != nil { + return handleReadError(d, "NsxtUpgradePrecheckAcknowledge", id, err) + } + err = setPrecheckWarningsInSchema(d, precheckWarnings) + if err != nil { + return handleReadError(d, "NsxtUpgradePrecheckAcknowledge", id, err) + } + return nil +} + +func setPrecheckWarningsInSchema(d *schema.ResourceData, precheckWarnings []nsxModel.UpgradeCheckFailure) error { + var precheckWarningList []map[string]interface{} + for _, precheckWarning := range precheckWarnings { + elem := make(map[string]interface{}) + elem["id"] = precheckWarning.Id + elem["message"] = precheckWarning.Message.Message + elem["is_acknowledged"] = precheckWarning.Acked + precheckWarningList = append(precheckWarningList, elem) + } + return d.Set("precheck_warnings", precheckWarningList) +} + +func resourceNsxtUpgradePrecheckAcknowledgeUpdate(d *schema.ResourceData, m interface{}) error { + id := d.Id() + precheckIDs := interface2StringList(d.Get("precheck_ids").([]interface{})) + err := acknowledgePrecheckWarnings(m, precheckIDs) + if err != nil { + return handleUpdateError("NsxtPrecheckAcknowledge", id, err) + } + return resourceNsxtUpgradePrecheckAcknowledgeRead(d, m) +} + +func resourceNsxtUpgradePrecheckAcknowledgeDelete(d *schema.ResourceData, m interface{}) error { + return nil +} + +func getAcknowledgedPrecheckIDs(m interface{}) ([]string, error) { + var result []string + typeParam := nsxModel.UpgradeCheckFailure_TYPE_WARNING + precheckWarnings, err := getPrecheckErrors(m, &typeParam) + if err != nil { + return result, err + } + for _, warning := range precheckWarnings { + acked := *warning.Acked + if acked { + result = append(result, *warning.Id) + } + } + return result, nil +} diff --git a/nsxt/resource_nsxt_upgrade_prepare.go b/nsxt/resource_nsxt_upgrade_prepare.go new file mode 100644 index 000000000..15ddbd276 --- /dev/null +++ b/nsxt/resource_nsxt_upgrade_prepare.go @@ -0,0 +1,493 @@ +/* Copyright © 2023 VMware, Inc. All Rights Reserved. + SPDX-License-Identifier: MPL-2.0 */ + +package nsxt + +import ( + "fmt" + "log" + "strings" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/vmware/vsphere-automation-sdk-go/lib/vapi/std/errors" + "github.com/vmware/vsphere-automation-sdk-go/services/nsxt-mp/nsx" + nsxModel "github.com/vmware/vsphere-automation-sdk-go/services/nsxt-mp/nsx/model" + "github.com/vmware/vsphere-automation-sdk-go/services/nsxt-mp/nsx/upgrade" + "github.com/vmware/vsphere-automation-sdk-go/services/nsxt-mp/nsx/upgrade/bundles" + "github.com/vmware/vsphere-automation-sdk-go/services/nsxt-mp/nsx/upgrade/eula" + "github.com/vmware/vsphere-automation-sdk-go/services/nsxt-mp/nsx/upgrade/pre_upgrade_checks" +) + +var precheckComponentTypes = []string{"EDGE", "HOST", "MP"} + +const bundleUploadTimeout int = 3600 +const ucUpgradeTimeout int = 3600 +const precheckTimeout int = 3600 + +func resourceNsxtUpgradePrepare() *schema.Resource { + return &schema.Resource{ + Create: resourceNsxtUpgradePrepareCreate, + Read: resourceNsxtUpgradePrepareRead, + Update: resourceNsxtUpgradePrepareUpdate, + Delete: resourceNsxtUpgradePrepareDelete, + + Schema: map[string]*schema.Schema{ + "version": { + Type: schema.TypeString, + Description: "Target upgrade version for NSX, format is x.x.x..., should include at least 3 digits, example: 4.1.2", + Optional: true, + }, + "upgrade_bundle_url": { + Type: schema.TypeString, + Description: "URL of the NSXT Upgrade bundle", + Required: true, + ForceNew: true, + }, + "precheck_bundle_url": { + Type: schema.TypeString, + Description: "URL of the NSXT Upgrade precheck bundle (Only applied to NSXT version >= 4.1.1)", + Optional: true, + }, + "accept_user_agreement": { + Type: schema.TypeBool, + Description: "Whether to accept the user agreement", + Required: true, + }, + "bundle_upload_timeout": { + Type: schema.TypeInt, + Description: "Timeout for uploading bundle in seconds", + Optional: true, + Default: bundleUploadTimeout, + }, + "uc_upgrade_timeout": { + Type: schema.TypeInt, + Description: "Timeout for upgrading upgrade coordinator in seconds", + Optional: true, + Default: ucUpgradeTimeout, + }, + "precheck_timeout": { + Type: schema.TypeInt, + Description: "Timeout for executing pre-upgrade checks in seconds", + Optional: true, + Default: precheckTimeout, + }, + "failed_prechecks": { + Type: schema.TypeList, + Description: "List of failed prechecks for the upgrade, including both warnings and errors", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "type": { + Type: schema.TypeString, + Description: "Type of the precheck failure (warning or error)", + Computed: true, + }, + "id": { + Type: schema.TypeString, + Description: "ID of failed precheck", + Computed: true, + }, + "message": { + Type: schema.TypeString, + Description: "Message of the failed precheck", + Computed: true, + }, + "needs_ack": { + Type: schema.TypeBool, + Description: "Boolean value which identifies if acknowledgement is required for the precheck", + Computed: true, + }, + "needs_resolve": { + Type: schema.TypeBool, + Description: "Boolean value identifies if resolution is required for the precheck", + Computed: true, + }, + "acked": { + Type: schema.TypeBool, + Description: "Boolean value which identifies if precheck has been acknowledged", + Computed: true, + }, + "resolution_status": { + Type: schema.TypeString, + Description: "The resolution status of the precheck failure", + Computed: true, + }, + }, + }, + Computed: true, + }, + }, + } +} + +func resourceNsxtUpgradePrepareCreate(d *schema.ResourceData, m interface{}) error { + id := d.Id() + if id == "" { + id = newUUID() + } + err := prepareForUpgrade(d, m) + if err != nil { + return handleCreateError("NsxtUpgradePrepare", id, err) + } + d.SetId(id) + return resourceNsxtUpgradePrepareRead(d, m) +} + +func prepareForUpgrade(d *schema.ResourceData, m interface{}) error { + // 1. Upload upgrade bundle and wait for upload to complete + err := uploadPrecheckAndUpgradeBundle(d, m) + if err != nil { + return logAPIError("Failed to upload bundle", err) + } + // 2. Accept eula + err = acceptUserAgreement(d, m) + if err != nil { + return err + } + // 3. Upgrade UC and check for its upgrade status + err = upgradeUc(d, m) + if err != nil { + return logAPIError("Failed to upgrade Upgrade Coordinator", err) + } + return nil +} + +func precheckNeeded(m interface{}) (bool, error) { + connector := getPolicyConnector(m) + summaryClient := upgrade.NewSummaryClient(connector) + summary, err := summaryClient.Get() + if err != nil { + return false, err + } + if summary.UpgradeCoordinatorUpdated == nil || !(*summary.UpgradeCoordinatorUpdated) { + log.Printf("Upgrade coordinated is not upgraded, skip running precheck") + return false, nil + } + if summary.UpgradeStatus == nil || (*summary.UpgradeStatus) != nsxModel.UpgradeSummary_UPGRADE_STATUS_NOT_STARTED { + log.Printf("Upgrade process has started, skip running precheck") + return false, nil + } + return true, nil +} + +func resourceNsxtUpgradePrepareRead(d *schema.ResourceData, m interface{}) error { + id := d.Id() + var err error + // Execute precheck in Read function if upload bundle has been uploaded and upgrade not started + precheckNeeded, err := precheckNeeded(m) + if err != nil { + return logAPIError("Failed to get previous precheck result", err) + } + if precheckNeeded { + previousAcknowledgedPrecheckIDs, err := getAcknowledgedPrecheckIDs(m) + if err != nil { + return logAPIError("Failed to get previous precheck result", err) + } + err = executePreupgradeChecks(d, m) + if err != nil { + return logAPIError("Failed to execute pre-upgrade checks", err) + } + err = acknowledgePrecheckWarnings(m, previousAcknowledgedPrecheckIDs) + if err != nil { + return err + } + } + precheckFailures, err := getPrecheckErrors(m, nil) + if err != nil { + return handleReadError(d, "NsxtUpgradePrepare", id, err) + } + err = setFailedPrechecksInSchema(d, precheckFailures) + if err != nil { + return handleReadError(d, "NsxtUpgradePrepare", id, err) + } + return nil +} + +func resourceNsxtUpgradePrepareUpdate(d *schema.ResourceData, m interface{}) error { + id := d.Id() + err := prepareForUpgrade(d, m) + if err != nil { + return handleUpdateError("NsxtUpgradePrepare", id, err) + } + return resourceNsxtUpgradePrepareRead(d, m) +} + +func resourceNsxtUpgradePrepareDelete(d *schema.ResourceData, m interface{}) error { + return nil +} + +func uploadPrecheckAndUpgradeBundle(d *schema.ResourceData, m interface{}) error { + upgradeBundleType := nsxModel.UpgradeBundleFetchRequest_BUNDLE_TYPE_UPGRADE + precheckBundleType := nsxModel.UpgradeBundleFetchRequest_BUNDLE_TYPE_PRE_UPGRADE + precheckBundleURL := d.Get("precheck_bundle_url").(string) + if !precheckBundleCompatibilityCheck(precheckBundleURL) { + return fmt.Errorf("Precheck bundle is only supported and is required for NSXT version >= 4.1.1") + } + if len(precheckBundleURL) > 0 { + err := uploadUpgradeBundle(d, m, precheckBundleType) + if err != nil { + return fmt.Errorf("Failed to upload precheck bundle: %s", err) + } + } + err := uploadUpgradeBundle(d, m, upgradeBundleType) + if err != nil { + return fmt.Errorf("Failed to upload upgrade bundle: %s", err) + } + return nil +} + +func precheckBundleCompatibilityCheck(precheckBundleURL string) bool { + if nsxVersionLower("4.1.1") && len(precheckBundleURL) > 0 { + return false + } + if nsxVersionHigherOrEqual("4.1.1") && len(precheckBundleURL) == 0 { + return false + } + return true +} + +func uploadUpgradeBundle(d *schema.ResourceData, m interface{}, bundleType string) error { + upgradeBundleURL := d.Get("upgrade_bundle_url").(string) + precheckBundleURL := d.Get("precheck_bundle_url").(string) + var url string + timeout := d.Get("bundle_upload_timeout").(int) + c := m.(nsxtClients) + userName := c.NsxtClientConfig.UserName + password := c.NsxtClientConfig.Password + connector := getPolicyConnector(m) + summaryClient := upgrade.NewSummaryClient(connector) + summary, err := summaryClient.Get() + if err != nil { + return err + } + if bundleType == nsxModel.UpgradeBundleFetchRequest_BUNDLE_TYPE_UPGRADE { + if !d.HasChange("upgrade_bundle_url") { + return nil + } + url = upgradeBundleURL + } else { + // Check if same precheck bundle has already been uploaded by comparing input target version + // with the precheck bundler version in upgrade summary + // version format is x.x.x...., contains at least 3 numbers + version := d.Get("version").(string) + if summary.PreUpgradeBundleVersion != nil && version != "" { + existingBundleVersion := *summary.PreUpgradeBundleVersion + arr := strings.Split(version, ".") + if len(arr) < 3 { + log.Printf("Invalid input version format, cannot compare version, input version format must be x.x.x....") + } else { + if strings.HasPrefix(existingBundleVersion, version) { + log.Printf("Precheck bundle already uploaded to NSX, skip uploading precheck bundle") + return nil + } + } + } + url = precheckBundleURL + } + client := upgrade.NewBundlesClient(connector) + bundleFetchRequest := nsxModel.UpgradeBundleFetchRequest{ + Url: &url, + } + if nsxVersionHigherOrEqual("4.1.1") { + bundleFetchRequest.BundleType = &bundleType + bundleFetchRequest.Password = &password + bundleFetchRequest.Username = &userName + } + bundleID, err := client.Create(bundleFetchRequest, nil) + if err != nil { + return fmt.Errorf("Failed to upload upgrade bundle of type %s: %v", bundleType, err) + } + return waitForBundleUpload(m, *bundleID.BundleId, timeout) +} + +func acceptUserAgreement(d *schema.ResourceData, m interface{}) error { + connector := getPolicyConnector(m) + acceptUserAgreement := d.Get("accept_user_agreement").(bool) + if !acceptUserAgreement { + return fmt.Errorf("To proceed with upgrade, you must accept user agreement") + } + client := eula.NewAcceptClient(connector) + err := client.Create() + if err != nil { + return err + } + return nil +} + +func upgradeUc(d *schema.ResourceData, m interface{}) error { + connector := getPolicyConnector(m) + summaryClient := upgrade.NewSummaryClient(connector) + summary, err := summaryClient.Get() + if err != nil { + return err + } + if *summary.UpgradeCoordinatorUpdated { + log.Printf("Upgrade coordinator already upgraded") + return nil + } + client := nsx.NewUpgradeClient(connector) + err = client.Upgradeuc() + if err != nil { + return err + } + timeout := d.Get("uc_upgrade_timeout").(int) + return waitForUcUpgrade(m, timeout) +} + +func executePreupgradeChecks(d *schema.ResourceData, m interface{}) error { + connector := getPolicyConnector(m) + client := nsx.NewUpgradeClient(connector) + err := client.Executepreupgradechecks(nil, nil, nil, nil, nil, nil) + if err != nil { + return err + } + timeout := d.Get("precheck_timeout").(int) + for _, componentType := range precheckComponentTypes { + log.Printf("Execute pre-upgrade check on %s", componentType) + err = waitForPrecheckComplete(m, componentType, timeout) + if err != nil { + return err + } + } + return nil +} + +func getPrecheckErrors(m interface{}, typeParam *string) ([]nsxModel.UpgradeCheckFailure, error) { + connector := getPolicyConnector(m) + client := pre_upgrade_checks.NewFailuresClient(connector) + resultList, err := client.List(nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, typeParam, nil, nil) + if err != nil { + return nil, err + } + return resultList.Results, nil +} + +func setFailedPrechecksInSchema(d *schema.ResourceData, precheckErros []nsxModel.UpgradeCheckFailure) error { + var failedPrechecksList []map[string]interface{} + for _, precheckError := range precheckErros { + elem := make(map[string]interface{}) + elem["id"] = precheckError.Id + elem["message"] = precheckError.Message.Message + elem["type"] = precheckError.Type_ + elem["needs_ack"] = precheckError.NeedsAck + elem["needs_resolve"] = precheckError.NeedsResolve + elem["acked"] = precheckError.Acked + elem["resolution_status"] = precheckError.ResolutionStatus + failedPrechecksList = append(failedPrechecksList, elem) + } + return d.Set("failed_prechecks", failedPrechecksList) +} + +func waitForBundleUpload(m interface{}, bundleID string, timeout int) error { + connector := getPolicyConnector(m) + client := bundles.NewUploadStatusClient(connector) + pendingStates := []string{ + nsxModel.UpgradeBundleUploadStatus_STATUS_UPLOADING, + nsxModel.UpgradeBundleUploadStatus_STATUS_VERIFYING, + } + targetStates := []string{ + nsxModel.UpgradeBundleUploadStatus_STATUS_SUCCESS, + nsxModel.UpgradeBundleUploadStatus_STATUS_FAILED, + } + stateConf := &resource.StateChangeConf{ + Pending: pendingStates, + Target: targetStates, + Refresh: func() (interface{}, string, error) { + state, err := client.Get(bundleID) + if err != nil { + msg := fmt.Sprintf("Error while retrieving upload status of bundle %s", bundleID) + return state, nsxModel.UpgradeBundleUploadStatus_STATUS_FAILED, logAPIError(msg, err) + } + + log.Printf("[DEBUG] Current status for uploading bundle %s is %s", bundleID, *state.Status) + + return state, *state.Status, nil + }, + Timeout: time.Duration(timeout) * time.Second, + MinTimeout: 1 * time.Second, + Delay: 1 * time.Second, + } + _, err := stateConf.WaitForState() + if err != nil { + return fmt.Errorf("Failed to upload bundle %s: %s", bundleID, err) + } + return nil +} + +func waitForUcUpgrade(m interface{}, timeout int) error { + connector := getPolicyConnector(m) + client := upgrade.NewUcUpgradeStatusClient(connector) + pendingStates := []string{ + nsxModel.UcUpgradeStatus_STATE_NOT_STARTED, + nsxModel.UcUpgradeStatus_STATE_IN_PROGRESS, + } + targetStates := []string{ + nsxModel.UcUpgradeStatus_STATE_SUCCESS, + nsxModel.UcUpgradeStatus_STATE_FAILED, + } + stateConf := &resource.StateChangeConf{ + Pending: pendingStates, + Target: targetStates, + Refresh: func() (interface{}, string, error) { + state, err := client.Get() + if err != nil { + if _, ok := err.(errors.InternalServerError); ok { + log.Printf("[DEBUG] Temporary upstream connection error, retry retrieving UC upgrade status") + } else { + return state, nsxModel.UcUpgradeStatus_STATE_FAILED, logAPIError("Error while retrieving UC upgrade status", err) + } + } + if state.State != nil { + log.Printf("[DEBUG] Current status for UC Upgrade is %s", *state.State) + return state, *state.State, nil + } + return state, nsxModel.UcUpgradeStatus_STATE_IN_PROGRESS, nil + }, + Timeout: time.Duration(timeout) * time.Second, + MinTimeout: 1 * time.Second, + Delay: 1 * time.Second, + } + _, err := stateConf.WaitForState() + if err != nil { + return fmt.Errorf("Failed to upgrade UC: %s", err) + } + return nil +} + +func waitForPrecheckComplete(m interface{}, componentType string, timeout int) error { + connector := getPolicyConnector(m) + client := upgrade.NewStatusSummaryClient(connector) + pendingStates := []string{ + nsxModel.UpgradeChecksExecutionStatus_STATUS_NOT_STARTED, + nsxModel.UpgradeChecksExecutionStatus_STATUS_IN_PROGRESS, + nsxModel.UpgradeChecksExecutionStatus_STATUS_ABORTING, + } + targetStates := []string{ + nsxModel.UpgradeChecksExecutionStatus_STATUS_ABORTED, + nsxModel.UpgradeChecksExecutionStatus_STATUS_COMPLETED, + } + stateConf := &resource.StateChangeConf{ + Pending: pendingStates, + Target: targetStates, + Refresh: func() (interface{}, string, error) { + state, err := client.Get(&componentType, nil, nil) + if err != nil { + return state, nsxModel.UpgradeChecksExecutionStatus_STATUS_ABORTED, logAPIError("Error retrieving component upgrade status", err) + } + componentStatus := state.ComponentStatus + if len(componentStatus) != 1 { + return state, nsxModel.UpgradeChecksExecutionStatus_STATUS_ABORTED, logAPIError("Error retrieving component upgrade status", err) + } + return state, *componentStatus[0].PreUpgradeStatus.Status, nil + }, + Timeout: time.Duration(timeout) * time.Second, + MinTimeout: 1 * time.Second, + Delay: 1 * time.Second, + } + _, err := stateConf.WaitForState() + if err != nil { + return fmt.Errorf("Encounter error while running precheck on component type %s: %s", componentType, err) + } + return nil +} diff --git a/website/docs/d/upgrade_prepare_ready.html.markdown b/website/docs/d/upgrade_prepare_ready.html.markdown new file mode 100644 index 000000000..7f3e52607 --- /dev/null +++ b/website/docs/d/upgrade_prepare_ready.html.markdown @@ -0,0 +1,31 @@ +--- +subcategory: "Beta" +layout: "nsxt" +page_title: "NSXT: upgrade_prepare_ready" +description: A data source to check if NSX Manager is ready for upgrade. +--- + +# nsxt_upgrade_prepare_ready + +This data source provides information about the upgrade readiness of NSX Manager. +The data source will be successfully created only if there are no errors in precheck and +all warnings have been acknowledged. +If there is any nsxt_upgrade_precheck_acknowledge resource, then nsxt_upgrade_prepare_ready resource +will only be successfully created if all nsxt_upgrade_precheck_acknowledge resources are +successfully created. +When created together with nsxt_upgrade_precheck_acknowledge resources, the dependency must be specified +via depends_on in nsxt_upgrade_prepare_ready data source's template. + +## Example Usage + +```hcl +data "nsxt_upgrade_prepare_ready" "test" { + depends_on = ["nsxt_upgrade_precheck_acknowledge.test"] + upgrade_prepare_id = nsxt_upgrade_prepare.test.id +} +``` + +## Argument Reference + +* `upgrade_prepare_id` - (Required) ID of corresponding `nsxt_upgrade_prepare` resource. +* `id` - (Optional) The ID the upgrade_prepare_status data source. diff --git a/website/docs/r/upgrade_precheck_acknowledge.html.markdown b/website/docs/r/upgrade_precheck_acknowledge.html.markdown new file mode 100644 index 000000000..e76eb00f4 --- /dev/null +++ b/website/docs/r/upgrade_precheck_acknowledge.html.markdown @@ -0,0 +1,33 @@ +--- +subcategory: "Beta" +layout: "nsxt" +page_title: "NSXT: nsxt_upgrade_precheck_acknowledge" +description: A resource to acknowledge failed NSXT upgrade prechecks. +--- + +# nsxt_upgrade_precheck_acknowledge + +This resource provides a method for acknowledging the failed prechecks +for NSXT upgrade. + +## Example Usage + +```hcl +resource "nsxt_upgrade_precheck_acknowledge" "test" { + precheck_ids = ["backupOperationCheck", "pUBCheck"] +} +``` + +## Argument Reference + +The following arguments are supported: + +* `precheck_ids` - (Required) List of ids of failed prechecks user wants to acknowledge. +* `precheck_warnings` - (Computed) State of all precheck warnings. + * `id` - ID of the precheck warning. + * `message` - Message of the precheck warning. + * `is_acknowledged` - Boolean value which identifies if the precheck warning has been acknowledged. + +## Importing + +Importing is not supported for this resource. diff --git a/website/docs/r/upgrade_prepare.html.markdown b/website/docs/r/upgrade_prepare.html.markdown new file mode 100644 index 000000000..f314febb2 --- /dev/null +++ b/website/docs/r/upgrade_prepare.html.markdown @@ -0,0 +1,60 @@ +--- +subcategory: "Beta" +layout: "nsxt" +page_title: "NSXT: nsxt_upgrade_prepare" +description: A resource to prepare for the upgrade of NSXT cluster. +--- + +# nsxt_upgrade_prepare + +This resource provides a method for preparing the upgrade of NSXT cluster, +it will upload upgrade bundle to NSX and run upgrade prechecks. +If there are errors in precheck result, the creation of resource will fail. +Precheck warnings will be listed in the `failed_prechecks` field of terraform state, +user can use the nsxt_upgrade_precheck_acknowledge resource to acknowledge these +warnings. +When using this resource to prepare for NSXT upgrade, the username and password +for NSXT must be provided in the nsxt provider config. + +## Example Usage + +```hcl +resource "nsxt_upgrade_prepare" "test" { + upgrade_bundle_url = "http://url-to-upgrade-bundle.mub" + precheck_bundle_url = "http://url-to-precheck-bundle.pub" + accept_user_agreement = true + bundle_upload_timeout = 1600 + uc_upgrade_timeout = 600 + precheck_timeout = 1600 + version = "4.1.2" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `upgrade_bundle_url` - (Required) The url to download the Manager Upgrade bundle. +* `version` - (Optional) Target upgrade version for NSX, format is x.x.x..., should include at least 3 digits, example: 4.1.2 +* `precheck_bundle_url` - (Optional) The url to download the Precheck bundle. This field is required if NSX >= 4.1.1 +* `accept_user_agreement` - (Required) This field must be set to true otherwise upgrade will not proceed. +* `bundle_upload_timeout` - (Optional) Timeout for uploading upgrade bundle in seconds. Default is `3600`. +* `uc_upgrade_timeout` - (Optional) Timeout for upgrading upgrade coordinator in seconds. Default is `1800`. +* `precheck_timeout` - (Optional) Timeout for executing pre-upgrade checks in seconds. Default is `3600`. + +## Argument Reference + +In addition to arguments listed above, the following attributes are exported: + +* `failed_prechecks` - (Computed) Failed prechecks from running pre-upgrade check. + * `id` - ID of the failed precheck. + * `message` - Message of the failed precheck. + * `type` - Type of the failed precheck, possible values are `warning` and `failure`. + * `needs_ack` - Boolean value which identifies if acknowledgement is required for the precheck. + * `needs_resolve` - Boolean value identifies if resolution is required for the precheck. + * `acked` - Boolean value which identifies if precheck has been acknowledged. + * `resolution_status` - The resolution status of precheck failure. + +## Importing + +Importing is not supported for this resource.