diff --git a/nsxt/data_source_nsxt_edge_upgrade_group.go b/nsxt/data_source_nsxt_edge_upgrade_group.go index 9ffb621d9..afed08617 100644 --- a/nsxt/data_source_nsxt_edge_upgrade_group.go +++ b/nsxt/data_source_nsxt_edge_upgrade_group.go @@ -16,6 +16,7 @@ import ( var ( edgeUpgradeGroup = "EDGE" hostUpgradeGroup = "HOST" + mpUpgradeGroup = "MP" timeoutUUGCreat = 5 * time.Minute intervalUUGCreat = 5 * time.Second diff --git a/nsxt/data_source_nsxt_upgrade_postcheck.go b/nsxt/data_source_nsxt_upgrade_postcheck.go new file mode 100644 index 000000000..21f00ee83 --- /dev/null +++ b/nsxt/data_source_nsxt_upgrade_postcheck.go @@ -0,0 +1,122 @@ +/* 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/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + "github.com/vmware/vsphere-automation-sdk-go/services/nsxt-mp/nsx/model" + "github.com/vmware/vsphere-automation-sdk-go/services/nsxt-mp/nsx/upgrade/upgrade_unit_groups" +) + +func dataSourceNsxtUpgradePostCheck() *schema.Resource { + return &schema.Resource{ + Read: dataSourceNsxtUpgradePostCheckRead, + + Schema: map[string]*schema.Schema{ + "upgrade_run_id": { + Type: schema.TypeString, + Description: "ID of corresponding nsxt_upgrade_run resource", + Required: true, + }, + "type": { + Type: schema.TypeString, + Description: "Component Type", + Required: true, + ValidateFunc: validation.StringInSlice([]string{edgeUpgradeGroup, hostUpgradeGroup}, false), + }, + "failed_group": { + Type: schema.TypeList, + Description: "Upgrade unit groups that failed in upgrade post-checks", + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Description: "Unique identifier of upgrade unit group", + Required: true, + }, + "status": { + Type: schema.TypeString, + Description: "Status of execution of upgrade post-checks", + Computed: true, + }, + "details": { + Type: schema.TypeString, + Description: "Details about current execution of upgrade post-checks", + Computed: true, + }, + "failure_count": { + Type: schema.TypeInt, + Description: "Total count of generated failures or warnings in last execution of upgrade post-checks", + Computed: true, + }, + }, + }, + }, + }, + } +} + +func dataSourceNsxtUpgradePostCheckRead(d *schema.ResourceData, m interface{}) error { + connector := getPolicyConnector(m) + aggregateInfoClient := upgrade_unit_groups.NewAggregateInfoClient(connector) + component := d.Get("type").(string) + + stateConf := &resource.StateChangeConf{ + Pending: []string{model.UpgradeChecksExecutionStatus_STATUS_IN_PROGRESS}, + Target: []string{model.UpgradeChecksExecutionStatus_STATUS_COMPLETED}, + Refresh: func() (interface{}, string, error) { + listRes, err := aggregateInfoClient.List(&component, nil, nil, nil, nil, nil, nil, nil) + if err != nil { + return nil, "", fmt.Errorf("failed to retrieve %s post check result: %v", err) + } + for _, res := range listRes.Results { + if *res.PostUpgradeStatus.Status == model.UpgradeChecksExecutionStatus_STATUS_IN_PROGRESS { + return res.PostUpgradeStatus, model.UpgradeChecksExecutionStatus_STATUS_IN_PROGRESS, nil + } + } + // Return UpgradeChecksExecutionStatus_STATUS_COMPLETED here doesn't mean all UpgradeUnitGroup post checks are completed. + // Just to stop polling the states as none of UpgradeUnitGroup is in in_progress state. + return listRes, model.UpgradeChecksExecutionStatus_STATUS_COMPLETED, nil + }, + Timeout: upgradeStatusCheckTimeout, + PollInterval: upgradeStatusCheckInterval, + Delay: upgradeStatusCheckDelay, + } + + _, err := stateConf.WaitForState() + if err != nil { + return err + } + + listRes, err := aggregateInfoClient.List(&component, nil, nil, nil, nil, nil, nil, nil) + if err != nil { + return fmt.Errorf("failed to retrieve %s post check result: %v", err) + } + var failedGroup []map[string]interface{} + for _, res := range listRes.Results { + var elem map[string]interface{} + failureCounts := 0 + if res.PostUpgradeStatus.FailureCount != nil { + failureCounts = int(*res.PostUpgradeStatus.FailureCount) + } else if res.PostUpgradeStatus.NodeWithIssuesCount != nil { + failureCounts = int(*res.PostUpgradeStatus.NodeWithIssuesCount) + } + if *res.PostUpgradeStatus.Status != model.UpgradeChecksExecutionStatus_STATUS_COMPLETED || failureCounts > 0 { + elem["id"] = *res.Id + elem["status"] = *res.PostUpgradeStatus.Status + elem["details"] = *res.PostUpgradeStatus.Details + elem["failure_count"] = failureCounts + failedGroup = append(failedGroup, elem) + } + } + if len(failedGroup) > 0 { + d.Set("failed_group", failedGroup) + } + + return nil +} diff --git a/nsxt/provider.go b/nsxt/provider.go index 28a5b8222..97e99377f 100644 --- a/nsxt/provider.go +++ b/nsxt/provider.go @@ -316,6 +316,7 @@ func Provider() *schema.Provider { "nsxt_discover_node": dataSourceNsxtDiscoverNode(), "nsxt_edge_upgrade_group": dataSourceNsxtEdgeUpgradeGroup(), "nsxt_host_upgrade_group": dataSourceNsxtHostUpgradeGroup(), + "nsxt_upgrade_postcheck": dataSourceNsxtUpgradePostCheck(), }, ResourcesMap: map[string]*schema.Resource{ @@ -469,6 +470,7 @@ func Provider() *schema.Provider { "nsxt_policy_lb_passive_monitor_profile": resourceNsxtPolicyLBPassiveMonitorProfile(), "nsxt_policy_lb_tcp_monitor_profile": resourceNsxtPolicyLBTcpMonitorProfile(), "nsxt_policy_lb_udp_monitor_profile": resourceNsxtPolicyLBUdpMonitorProfile(), + "nsxt_upgrade_run": resourceNsxtUpgradeRun(), }, ConfigureFunc: providerConfigure, diff --git a/nsxt/resource_nsxt_upgrade_run.go b/nsxt/resource_nsxt_upgrade_run.go new file mode 100644 index 000000000..bbee60a93 --- /dev/null +++ b/nsxt/resource_nsxt_upgrade_run.go @@ -0,0 +1,570 @@ +/* Copyright © 2024 VMware, Inc. All Rights Reserved. + SPDX-License-Identifier: MPL-2.0 */ + +package nsxt + +import ( + "fmt" + "log" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + "github.com/vmware/vsphere-automation-sdk-go/runtime/protocol/client" + "github.com/vmware/vsphere-automation-sdk-go/services/nsxt-mp/nsx" + "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/plan" +) + +// Order matters +var upgradeComponentList = []string{ + edgeUpgradeGroup, + hostUpgradeGroup, + mpUpgradeGroup, +} + +var componentToGroupKey = map[string]string{ + edgeUpgradeGroup: "edge_group", + hostUpgradeGroup: "host_group", +} + +var componentToSettingKey = map[string]string{ + edgeUpgradeGroup: "edge_upgrade_setting", + hostUpgradeGroup: "host_upgrade_setting", +} + +var supportedUpgradeGroupExtendedConfigValue = map[string][]string{ + "upgrade_mode": {"maintenance_mode", "in_place", "stage_in_vlcm"}, + "maintenance_mode_config_vsan_mode": {"evacuate_all_data", "ensure_object_accessibility", "no_action"}, + "maintenance_mode_config_evacuate_powered_off_vms": {"true", "false"}, + "rebootless_upgrade": {"true", "false"}, +} + +var ( + upgradeStatusCheckInterval = 2 * time.Minute + upgradeStatusCheckTimeout = 60 * time.Minute + upgradeStatusCheckDelay = 2 * time.Minute +) + +var modifiableComponentUpgradeStatus = []string{ + model.ComponentUpgradeStatus_STATUS_FAILED, + model.ComponentUpgradeStatus_STATUS_NOT_STARTED, + model.ComponentUpgradeStatus_STATUS_PAUSED, +} + +var inFlightComponentUpgradeStatus = []string{ + model.ComponentUpgradeStatus_STATUS_IN_PROGRESS, + model.ComponentUpgradeStatus_STATUS_PAUSING, +} + +type upgradeClientSet struct { + GroupClient upgrade.UpgradeUnitGroupsClient + SettingClient plan.SettingsClient + PlanClient upgrade.PlanClient + StatusClient upgrade.StatusSummaryClient + UpgradeClient nsx.UpgradeClient + + edgeUpgradeStatus string + hostUpgradeStatus string +} + +func newUpgradeClientSet(connector client.Connector) upgradeClientSet { + return upgradeClientSet{ + GroupClient: upgrade.NewUpgradeUnitGroupsClient(connector), + SettingClient: plan.NewSettingsClient(connector), + PlanClient: upgrade.NewPlanClient(connector), + StatusClient: upgrade.NewStatusSummaryClient(connector), + UpgradeClient: nsx.NewUpgradeClient(connector), + } +} + +func resourceNsxtUpgradeRun() *schema.Resource { + return &schema.Resource{ + Create: resourceNsxtUpgradeRunCreate, + Read: resourceNsxtUpgradeRunRead, + Update: resourceNsxtUpgradeRunUpdate, + Delete: resourceNsxtUpgradeRunDelete, + + Schema: map[string]*schema.Schema{ + "upgrade_prepare_id": { + Type: schema.TypeString, + Description: "ID of corresponding nsxt_upgrade_prepare resource", + Required: true, + }, + "edge_group": getUpgradeGroupSchema(false), + "host_group": getUpgradeGroupSchema(false), + "edge_upgrade_setting": getUpgradeSettingSchema(true), + "host_upgrade_setting": getUpgradeSettingSchema(false), + "upgrade_plan": getUpgradeGroupSchema(true), + "state": { + Type: schema.TypeList, + Description: "Upgrade states", + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "type": { + Type: schema.TypeString, + Description: "Component type", + Computed: true, + }, + "status": { + Type: schema.TypeString, + Description: "Upgrade status of component", + Computed: true, + }, + "percent_complete": { + Type: schema.TypeInt, + Description: "Indicator of upgrade progress in percentage", + Computed: true, + }, + "details": { + Type: schema.TypeString, + Description: "Details about the upgrade status", + Computed: true, + }, + "target_version": { + Type: schema.TypeString, + Description: "Target component version", + Computed: true, + }, + }, + }, + }, + }, + } +} + +func getUpgradeGroupSchema(compute bool) *schema.Schema { + elemSchema := map[string]*schema.Schema{ + "upgrade_mode": { + Type: schema.TypeString, + Description: "Upgrade mode", + Optional: true, + Computed: compute, + ValidateFunc: validation.StringInSlice(supportedUpgradeGroupExtendedConfigValue["upgrade_mode"], false), + }, + "maintenance_mode_config_vsan_mode": { + Type: schema.TypeString, + Description: "Maintenance mode config vsan mode", + Optional: true, + Computed: compute, + ValidateFunc: validation.StringInSlice(supportedUpgradeGroupExtendedConfigValue["maintenance_mode_config_vsan_mode"], false), + }, + "maintenance_mode_config_evacuate_powered_off_vms": { + Type: schema.TypeString, + Description: "Maintenance mode config evacuate powered off vms", + Optional: true, + Computed: compute, + ValidateFunc: validation.StringInSlice(supportedUpgradeGroupExtendedConfigValue["maintenance_mode_config_evacuate_powered_off_vms"], false), + }, + "rebootless_upgrade": { + Type: schema.TypeString, + Description: "Rebootless upgrade", + Optional: true, + Computed: compute, + ValidateFunc: validation.StringInSlice(supportedUpgradeGroupExtendedConfigValue["rebootless_upgrade"], false), + }, + } + if compute { + elemSchema["type"] = &schema.Schema{ + Type: schema.TypeString, + Description: "Component type", + Computed: true, + } + elemSchema["id"] = &schema.Schema{ + Type: schema.TypeString, + Description: "ID of upgrade unit group", + Computed: true, + } + elemSchema["enabled"] = &schema.Schema{ + Type: schema.TypeBool, + Description: "Flag to indicate whether upgrade of this group is enabled or not", + Computed: true, + } + elemSchema["parallel"] = &schema.Schema{ + Type: schema.TypeBool, + Description: "Upgrade method to specify whether the upgrade is to be performed in parallel or serially", + Computed: true, + } + elemSchema["pause_after_each_upgrade_unit"] = &schema.Schema{ + Type: schema.TypeBool, + Description: "Flag to indicate whether upgrade should be paused after upgrade of each upgrade-unit", + Computed: true, + } + } else { + elemSchema["id"] = &schema.Schema{ + Type: schema.TypeString, + Description: "ID of upgrade unit group", + Required: true, + } + elemSchema["enabled"] = &schema.Schema{ + Type: schema.TypeBool, + Description: "Flag to indicate whether upgrade of this group is enabled or not", + Optional: true, + Default: true, + } + elemSchema["parallel"] = &schema.Schema{ + Type: schema.TypeBool, + Description: "Upgrade method to specify whether the upgrade is to be performed in parallel or serially", + Optional: true, + Default: true, + } + elemSchema["pause_after_each_upgrade_unit"] = &schema.Schema{ + Type: schema.TypeBool, + Description: "Flag to indicate whether upgrade should be paused after upgrade of each upgrade-unit", + Optional: true, + Default: false, + } + } + return &schema.Schema{ + Type: schema.TypeList, + Description: "Upgrade group for this upgrade", + Optional: true, + Computed: compute, + Elem: &schema.Resource{ + Schema: elemSchema, + }, + } +} + +func getUpgradeSettingSchema(isEdge bool) *schema.Schema { + elemSchema := map[string]*schema.Schema{ + "post_upgrade_check": { + Type: schema.TypeBool, + Description: "Whether run post upgrade check", + Optional: true, + Default: true, + }, + "parallel": { + Type: schema.TypeBool, + Description: "Whether run upgrade parallel", + Optional: true, + Default: true, + }, + "stop_on_error": { + Type: schema.TypeBool, + Description: "Whether stop the upgrade when an error occur", + Optional: true, + Default: false, + }, + } + if isEdge { + // Edge Upgrade setting is forced to stop on error. + delete(elemSchema, "stop_on_error") + } + return &schema.Schema{ + Type: schema.TypeList, + Description: "Upgrade plan setting for component", + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: elemSchema, + }, + } +} + +func resourceNsxtUpgradeRunCreate(d *schema.ResourceData, m interface{}) error { + id := d.Id() + if id == "" { + id = newUUID() + } + connector := getPolicyConnector(m) + upgradeClientSet := newUpgradeClientSet(connector) + + log.Printf("[INFO] Updating UpgradeUnitGroup and UpgradePlanSetting.") + err := prepareUpgrade(upgradeClientSet, d, true) + if err != nil { + return handleCreateError("NsxtUpgradeRun", id, err) + } + + log.Printf("[INFO] Successfully update UpgradeUnitGroup and UpgradePlanSetting. Start Upgrade.") + + err = runUpgrade(upgradeClientSet, d) + if err != nil { + log.Printf("[WARN] Upgrade didn't success: %v. Refer to the state of resource for more information.", err) + } + + d.SetId(id) + return resourceNsxtUpgradeRunRead(d, m) +} + +func prepareUpgrade(upgradeClientSet upgradeClientSet, d *schema.ResourceData, create bool) error { + for _, component := range upgradeComponentList { + // Customize MP upgrade is not allowed + if component == mpUpgradeGroup { + continue + } + + if !create { + if !d.HasChanges(componentToGroupKey[component]) && !d.HasChange(componentToSettingKey[component]) { + continue + } + status, err := getComponentUpgradeStatus(upgradeClientSet.StatusClient, component) + if err != nil { + return err + } + if status == model.ComponentUpgradeStatus_STATUS_SUCCESS { + continue + } + err = waitComponentUpgradeForStatus(upgradeClientSet.StatusClient, component, inFlightComponentUpgradeStatus, modifiableComponentUpgradeStatus) + if err != nil { + return err + } + } + + // Call reset regardless it is create or not, because we don't know if UpgradeUnitGroup or UpgradePlanSetting has been changed. + err := upgradeClientSet.PlanClient.Reset(component) + if err != nil { + return err + } + + err = updateUpgradeUnitGroups(upgradeClientSet, d, component) + if err != nil { + return err + } + + err = updateComponentUpgradePlanSetting(upgradeClientSet.SettingClient, d, component) + if err != nil { + return err + } + } + return nil +} + +func getComponentUpgradeStatus(statusClient upgrade.StatusSummaryClient, component string) (string, error) { + status, err := statusClient.Get(&component, nil, nil) + if err != nil { + return "", fmt.Errorf("fail to retrieve upgrade status of %s component: %v", component, err) + } + for _, componentStatus := range status.ComponentStatus { + if *componentStatus.ComponentType == component { + return *componentStatus.Status, nil + } + } + return "", fmt.Errorf("fail to retrieve upgrade status of %s component", component) +} + +func waitComponentUpgradeForStatus(statusClient upgrade.StatusSummaryClient, component string, pending, target []string) error { + stateConf := &resource.StateChangeConf{ + Pending: pending, + Target: target, + Refresh: func() (interface{}, string, error) { + status, err := getComponentUpgradeStatus(statusClient, component) + if err != nil { + return nil, model.ComponentUpgradeStatus_STATUS_FAILED, err + } + log.Printf("[DEBUG] Current status for %s Upgrade is %s", component, status) + return nil, status, nil + }, + Timeout: upgradeStatusCheckTimeout, + PollInterval: upgradeStatusCheckInterval, + Delay: upgradeStatusCheckDelay, + } + _, err := stateConf.WaitForState() + if err != nil { + return fmt.Errorf("failed to wait %s Upgrade to be %s: %v", component, target, err) + } + return nil +} + +func updateUpgradeUnitGroups(upgradeClientSet upgradeClientSet, d *schema.ResourceData, component string) error { + isBefore := false + getReorderAfterReq := func(id string) model.ReorderRequest { + return model.ReorderRequest{ + Id: &id, + IsBefore: &isBefore, + } + } + + preUpgradeGroupID := "" + for _, groupI := range d.Get(componentToGroupKey[component]).([]interface{}) { + group := groupI.(map[string]interface{}) + groupID := group["id"].(string) + groupGet, err := upgradeClientSet.GroupClient.Get(groupID, nil) + if err != nil { + return err + } + + enabled := group["enabled"].(bool) + pause := group["pause_after_each_upgrade_unit"].(bool) + groupGet.Enabled = &enabled + groupGet.PauseAfterEachUpgradeUnit = &pause + + // Parallel can't be modified for EDGE upgrade unit group + if component != edgeUpgradeGroup { + parallel := group["parallel"].(bool) + groupGet.Parallel = ¶llel + } + + upgradeMode := group["upgrade_mode"].(string) + mmcVsanMode := group["maintenance_mode_config_vsan_mode"].(string) + mmcEvacuateOffVms := group["maintenance_mode_config_evacuate_powered_off_vms"].(string) + rebootlessUpgrade := group["rebootless_upgrade"].(string) + var extendConfig []model.KeyValuePair + if upgradeMode != "" { + upgradeModeKey := "upgrade_mode" + extendConfig = append(extendConfig, model.KeyValuePair{&upgradeModeKey, &upgradeMode}) + } + if mmcVsanMode != "" { + mmcVsanModeKey := "maintenance_mode_config_vsan_mode" + extendConfig = append(extendConfig, model.KeyValuePair{&mmcVsanModeKey, &mmcVsanMode}) + } + if mmcEvacuateOffVms != "" { + mmcEvacuateOffVmsKey := "maintenance_mode_config_evacuate_powered_off_vms" + extendConfig = append(extendConfig, model.KeyValuePair{&mmcEvacuateOffVmsKey, &mmcEvacuateOffVms}) + } + if rebootlessUpgrade != "" { + rebootlessUpgradeKey := "rebootless_upgrade" + extendConfig = append(extendConfig, model.KeyValuePair{&rebootlessUpgradeKey, &rebootlessUpgrade}) + } + groupGet.ExtendedConfiguration = extendConfig + + _, err = upgradeClientSet.GroupClient.Update(groupID, groupGet) + if err != nil { + return err + } + + if preUpgradeGroupID != "" { + err = upgradeClientSet.GroupClient.Reorder(groupID, getReorderAfterReq(preUpgradeGroupID)) + if err != nil { + return err + } + } + preUpgradeGroupID = groupID + } + return nil +} + +func updateComponentUpgradePlanSetting(settingClient plan.SettingsClient, d *schema.ResourceData, component string) error { + settingI := d.Get(componentToSettingKey[component]).([]interface{}) + if len(settingI) == 0 { + return nil + } + + settingGet, err := settingClient.Get(component) + if err != nil { + return err + } + + setting := settingI[0].(map[string]interface{}) + parallel := setting["parallel"].(bool) + settingGet.Parallel = ¶llel + + // PauseOnError can't be modified for EDGE upgrade setting + if component != edgeUpgradeGroup { + stopOnErr := setting["stop_on_error"].(bool) + settingGet.PauseOnError = &stopOnErr + } + + _, err = settingClient.Update(component, settingGet) + return err +} + +func runUpgrade(upgradeClientSet upgradeClientSet, d *schema.ResourceData) error { + for _, component := range upgradeComponentList { + upgradeClientSet.PlanClient.Upgrade(&component) + err := waitComponentUpgradeForStatus(upgradeClientSet.StatusClient, component, []string{model.ComponentUpgradeStatus_STATUS_IN_PROGRESS}, []string{model.ComponentUpgradeStatus_STATUS_SUCCESS}) + if err != nil { + return err + } + + //Post upgrade check + if component != mpUpgradeGroup { + settingI := d.Get(componentToSettingKey[component]).([]interface{}) + if len(settingI) == 0 { + continue + } + + setting := settingI[0].(map[string]interface{}) + postCheck := setting["post_upgrade_check"].(bool) + + if postCheck { + upgradeClientSet.UpgradeClient.Executepostupgradechecks(component) + } + } + } + return nil +} + +func setOutput(upgradeClientSet upgradeClientSet, d *schema.ResourceData) error { + results, err := upgradeClientSet.GroupClient.List(nil, nil, nil, nil, nil, nil, nil, nil) + if err != nil { + return err + } + + var plans []map[string]interface{} + for _, result := range results.Results { + elem := make(map[string]interface{}) + elem["id"] = result.Id + elem["parallel"] = result.Parallel + elem["enabled"] = result.Enabled + elem["pause_after_each_upgrade_unit"] = result.PauseAfterEachUpgradeUnit + elem["type"] = result.Type_ + if *result.Type_ != mpUpgradeGroup { + for _, config := range result.ExtendedConfiguration { + for k := range supportedUpgradeGroupExtendedConfigValue { + if k == *config.Key { + elem[*config.Key] = *config.Value + } + } + } + } + plans = append(plans, elem) + } + d.Set("upgrade_plan", plans) + + status, err := upgradeClientSet.StatusClient.Get(nil, nil, nil) + if err != nil { + return err + } + var states []map[string]interface{} + for _, result := range status.ComponentStatus { + elem := make(map[string]interface{}) + elem["type"] = result.ComponentType + elem["status"] = result.Status + elem["percent_complete"] = result.PercentComplete + elem["details"] = result.Details + elem["target_version"] = result.TargetComponentVersion + states = append(states, elem) + } + d.Set("state", states) + return nil +} + +func resourceNsxtUpgradeRunRead(d *schema.ResourceData, m interface{}) error { + id := d.Id() + connector := getPolicyConnector(m) + upgradeClientSet := newUpgradeClientSet(connector) + err := setOutput(upgradeClientSet, d) + if err != nil { + return handleReadError(d, "NsxtUpgradeRun", id, err) + } + return nil +} + +func resourceNsxtUpgradeRunUpdate(d *schema.ResourceData, m interface{}) error { + id := d.Id() + connector := getPolicyConnector(m) + upgradeClientSet := newUpgradeClientSet(connector) + + log.Printf("[INFO] Updating UpgradeUnitGroup and UpgradePlanSetting.") + err := prepareUpgrade(upgradeClientSet, d, false) + if err != nil { + return handleUpdateError("NsxtUpgradeRun", id, err) + } + + log.Printf("[INFO] Successfully update UpgradeUnitGroup and UpgradePlanSetting. Start Upgrade.") + + err = runUpgrade(upgradeClientSet, d) + if err != nil { + log.Printf("[WARN] Upgrade didn't success: %v. Refer to the state of resource for more information.", err) + } + + return resourceNsxtUpgradeRunRead(d, m) +} + +func resourceNsxtUpgradeRunDelete(d *schema.ResourceData, m interface{}) error { + return nil +} diff --git a/website/docs/d/post_upgrade_check.html.markdown b/website/docs/d/post_upgrade_check.html.markdown new file mode 100644 index 000000000..9c2401c5d --- /dev/null +++ b/website/docs/d/post_upgrade_check.html.markdown @@ -0,0 +1,34 @@ +--- +subcategory: "Beta" +layout: "nsxt" +page_title: "NSXT: edge_upgrade_group" +description: An Edge Upgrade Group data source. +--- + +# nsxt_upgrade_postcheck + +This data source provides information about Upgrade post-check results. + +## Example Usage + +```hcl +data "nsxt_upgrade_postcheck" "pc" { + upgrade_run_id = nsxt_upgrade_run.test.id + type = "EDGE" +} +``` + +## Argument Reference + +* `upgrade_run_id` - (Required) The ID of corresponding `nsxt_upgrade_run` resource. +* `type` - (Required) Component type of upgrade post-checks. + +## Attributes Reference + +In addition to arguments listed above, the following attributes are exported: + +* `failed_group` - (Computed) The description of the resource. + * `id` - ID of upgrade unit group. + * `status` - Status of execution of upgrade post-checks. + * `details` - Details about current execution of upgrade post-checks. + * `failure_count` - Total count of generated failures or warnings in last execution of upgrade post-checks }, diff --git a/website/docs/r/upgrade_run.html.markdown b/website/docs/r/upgrade_run.html.markdown new file mode 100644 index 000000000..0e40a5fa6 --- /dev/null +++ b/website/docs/r/upgrade_run.html.markdown @@ -0,0 +1,110 @@ +--- +subcategory: "Beta" +layout: "nsxt" +page_title: "NSXT: nsxt_upgrade_run" +description: A resource to configure and execute upgrade of NSXT cluster. +--- + +# nsxt_upgrade_run + +This resource provides a method to configure and execute upgrade of NSXT cluster. + +It will first configure upgrade unit groups and upgrade plan settings for `EDGE` +and `HOST` components. If any error occurs during this process, the creation of +the resource will fail. + +Then it will execute the upgrade for each component. If the upgrade fails during +this process, this resource will still be created and user could refer to the state of +this resource for more details of failures. + +If upgrade post-checks are configured to be run, it will trigger the upgrade post-check. +Please use data source `nsxt_upgrade_postcheck` to retrieve results of upgrade post-checks. + +## Example Usage + +```hcl +resource "nsxt_upgrade_run" "run1" { + upgrade_prepare_id = nsxt_upgrade_prepare.test.id + + edge_group { + id = data.nsxt_edge_upgrade_group.eg1.id + enabled = false + } + + edge_group { + id = data.nsxt_edge_upgrade_group.eg2.id + enabled = true + } + + host_group { + id = data.nsxt_edge_upgrade_group.eg2.id + parallel = true + } + + edge_upgrade_setting { + parallel = true + post_upgrade_check = true + } + + host_upgrade_setting { + parallel = true + post_upgrade_check = true + } +} +``` + +## Argument Reference + +The following arguments are supported: + +* `upgrade_prepare_id` - (Required) ID of corresponding `nsxt_upgrade_prepare` resource. +* `edge_group` - (Optional) EDGE component upgrade unit group configurations. Groups will be reordered following the order they present in this field. + * `id` - (Required) ID of the upgrade unit group. + * `enabled` - (Optional) Flag to indicate whether upgrade of this group is enabled or not. Default: True. + * `parallel` - (Optional) Upgrade method to specify whether the upgrade is to be performed in parallel or serially. Default: True. + * `pause_after_each_upgrade_unit` - (Optional) Flag to indicate whether upgrade should be paused after upgrade of each upgrade-unit. Default: False. + * `upgrade_mode` - (Optional) Upgrade mode. Supported values: `maintenance_mode`, `in_place`, `stage_in_vlcm`. + * `maintenance_mode_config_vsan_mode` - (Optional) Maintenance mode config of vsan mode. Supported values: `evacuate_all_data`, `ensure_object_accessibility`, `no_action`. + * `maintenance_mode_config_evacuate_powered_off_vms` - (Optional) Maintenance mode config of whether evacuate powered off vms. Supported values: `true`, `false`. + * `rebootless_upgrade` - (Optional) Flag to indicate whether to use rebootless upgrade. Supported values: `true`, `false`. +* `host_group` - (Optional) HOST component upgrade unit group configurations. Groups will be reordered following the order they present in this field. + * `id` - (Required) ID of the upgrade unit group. + * `enabled` - (Optional) Flag to indicate whether upgrade of this group is enabled or not. Default: True. + * `parallel` - (Optional) Upgrade method to specify whether the upgrade is to be performed in parallel or serially. Default: True. + * `pause_after_each_upgrade_unit` - (Optional) Flag to indicate whether upgrade should be paused after upgrade of each upgrade-unit. Default: False. + * `upgrade_mode` - (Optional) Upgrade mode. Supported values: `maintenance_mode`, `in_place`, `stage_in_vlcm`. + * `maintenance_mode_config_vsan_mode` - (Optional) Maintenance mode config of vsan mode. Supported values: `evacuate_all_data`, `ensure_object_accessibility`, `no_action`. + * `maintenance_mode_config_evacuate_powered_off_vms` - (Optional) Maintenance mode config of whether evacuate powered off vms. Supported values: `true`, `false`. + * `rebootless_upgrade` - (Optional) Whether to use rebootless upgrade. Supported values: `true`, `false`. +* `edge_upgrade_setting` - (Optional) EDGE component upgrade plan setting. + * `parallel` - (Optional) Upgrade Method to specify whether the upgrade is to be performed serially or in parallel. Default: True. + * `post_upgrade_check` - (Optional) Flag to indicate whether run post upgrade check after upgrade. Default: True. +* `host_upgrade_setting` - (Optional) HOST component upgrade plan setting. + * `parallel` - (Optional) Upgrade Method to specify whether the upgrade is to be performed serially or in parallel. Default: True. + * `post_upgrade_check` - (Optional) Flag to indicate whether run post upgrade check after upgrade. Default: True. + * `stop_on_error` - (Optional) Flag to indicate whether to pause the upgrade plan execution when an error occurs. Default: False. + +## Argument Reference + +In addition to arguments listed above, the following attributes are exported: + +* `upgrade_plan` - (Computed) Upgrade plan for current upgrade. Upgrade unit groups that are not defined in `edge_group` or `host_group` will also be included here. + * `type` - Component type. + * `id` - ID of the upgrade unit group. + * `enabled` - Flag to indicate whether upgrade of this group is enabled or not. + * `parallel` - Upgrade method to specify whether the upgrade is to be performed in parallel or serially. + * `pause_after_each_upgrade_unit` - Flag to indicate whether upgrade should be paused after upgrade of each upgrade-unit. + * `upgrade_mode` - Upgrade mode. + * `maintenance_mode_config_vsan_mode` - Maintenance mode config of vsan mode. + * `maintenance_mode_config_evacuate_powered_off_vms` - Maintenance mode config of whether evacuate powered off vms. + * `rebootless_upgrade` - Flag to indicate whether to use rebootless upgrade. +* `state` - (Computed) Upgrade states of each component + * `type` - Component type. + * `status` - Upgrade status of component. + * `percent_complete` - Indicator of upgrade progress in percentage. + * `details` - Details about the upgrade status. + * `target_version` - Target component version + +## Importing + +Importing is not supported for this resource.