diff --git a/docs/data-sources/gaussdb_mysql_slow_logs.md b/docs/data-sources/gaussdb_mysql_slow_logs.md new file mode 100644 index 0000000000..f07463a324 --- /dev/null +++ b/docs/data-sources/gaussdb_mysql_slow_logs.md @@ -0,0 +1,84 @@ +--- +subcategory: "GaussDB(for MySQL)" +layout: "huaweicloud" +page_title: "HuaweiCloud: huaweicloud_gaussdb_mysql_slow_logs" +description: |- + Use this data source to get the list of GaussDB MySQL slow logs. +--- + +# huaweicloud_gaussdb_mysql_slow_logs + +Use this data source to get the list of GaussDB MySQL slow logs. + +## Example Usage + +```hcl +variable "instance_id" {} +variable "node_id" {} +variable "start_time" {} +variable "end_time" {} + +data "huaweicloud_gaussdb_mysql_slow_logs" "test" { + instance_id = var.instance_id + node_id = var.node_id + start_time = var.start_time + end_time = var.end_time +} +``` + +## Argument Reference + +The following arguments are supported: + +* `region` - (Optional, String) Specifies the region in which to query the resource. + If omitted, the provider-level region will be used. + +* `instance_id` - (Required, String) Specifies the ID of the GaussDB MySQL instance. + +* `node_id` - (Required, String) Specifies the ID of the instance node. + +* `start_time` - (Required, String) Specifies the start time in the **yyyy-mm-ddThh:mm:ssZ** format. + +* `end_time` - (Required, String) Specifies the end time in the **yyyy-mm-ddThh:mm:ssZ** format. + +* `operate_type` - (Optional, String) Specifies the SQL statement type. Value options: **INSERT**, **UPDATE**, **SELECT**, + **DELETE**, **CREATE**, **ALTER**, **DROP**. + +* `database` - (Optional, String) Specifies the name of the database. + +## Attribute Reference + +In addition to all arguments above, the following attributes are exported: + +* `id` - The data source ID. + +* `slow_log_list` - Indicates the list of the slow logs. + + The [slow_log_list](#slow_log_list_struct) structure is documented below. + + +The `slow_log_list` block supports: + +* `count` - Indicates the ID of the instance node. + +* `count` - Indicates the number of executions. + +* `time` - Indicates the execution time. + +* `lock_time` - Indicates the lock wait time. + +* `rows_sent` - Indicates the number of sent rows. + +* `rows_examined` - Indicates the number of scanned rows. + +* `database` - Indicates the database that slow query logs belong to. + +* `users` - Indicates the name of the account. + +* `query_sample` - Indicates the execution syntax. + +* `type` - Indicates the statement type. + +* `start_time` - Indicates the start time in the **yyyy-mm-ddThh:mm:ssZ** format. + +* `client_ip` - Indicates the IP address of the client. diff --git a/huaweicloud/provider.go b/huaweicloud/provider.go index 3118bf330e..3f8da57e65 100644 --- a/huaweicloud/provider.go +++ b/huaweicloud/provider.go @@ -779,6 +779,7 @@ func Provider() *schema.Provider { "huaweicloud_gaussdb_mysql_restored_tables": gaussdb.DataSourceGaussdbMysqlRestoredTables(), "huaweicloud_gaussdb_mysql_project_quotas": gaussdb.DataSourceGaussdbMysqlProjectQuotas(), "huaweicloud_gaussdb_mysql_scheduled_tasks": gaussdb.DataSourceGaussDBMysqlScheduledTasks(), + "huaweicloud_gaussdb_mysql_slow_logs": gaussdb.DataSourceGaussDBMysqlSlowLogs(), "huaweicloud_gaussdb_influx_instances": gaussdb.DataSourceGaussDBInfluxInstances(), diff --git a/huaweicloud/services/acceptance/acceptance.go b/huaweicloud/services/acceptance/acceptance.go index 057c9be1aa..7f69df1079 100644 --- a/huaweicloud/services/acceptance/acceptance.go +++ b/huaweicloud/services/acceptance/acceptance.go @@ -163,12 +163,15 @@ var ( HW_BUILD_IMAGE_URL_UPDATED = os.Getenv("HW_BUILD_IMAGE_URL_UPDATED") // SWR Image URL for component deployment update HW_GAUSSDB_MYSQL_INSTANCE_ID = os.Getenv("HW_GAUSSDB_MYSQL_INSTANCE_ID") + HW_GAUSSDB_MYSQL_NODE_ID = os.Getenv("HW_GAUSSDB_MYSQL_NODE_ID") HW_GAUSSDB_MYSQL_DATABASE_NAME = os.Getenv("HW_GAUSSDB_MYSQL_DATABASE_NAME") HW_GAUSSDB_MYSQL_TABLE_NAME = os.Getenv("HW_GAUSSDB_MYSQL_TABLE_NAME") HW_GAUSSDB_MYSQL_INSTANCE_CONFIGURATION_ID = os.Getenv("HW_GAUSSDB_MYSQL_INSTANCE_CONFIGURATION_ID") HW_GAUSSDB_MYSQL_BACKUP_BEGIN_TIME = os.Getenv("HW_GAUSSDB_MYSQL_BACKUP_BEGIN_TIME") HW_GAUSSDB_MYSQL_BACKUP_END_TIME = os.Getenv("HW_GAUSSDB_MYSQL_BACKUP_END_TIME") HW_GAUSSDB_MYSQL_JOB_ID = os.Getenv("HW_GAUSSDB_MYSQL_JOB_ID") + HW_GAUSSDB_MYSQL_START_TIME = os.Getenv("HW_GAUSSDB_MYSQL_START_TIME") + HW_GAUSSDB_MYSQL_END_TIME = os.Getenv("HW_GAUSSDB_MYSQL_END_TIME") HW_VOD_WATERMARK_FILE = os.Getenv("HW_VOD_WATERMARK_FILE") HW_VOD_MEDIA_ASSET_FILE = os.Getenv("HW_VOD_MEDIA_ASSET_FILE") @@ -1205,6 +1208,13 @@ func TestAccPreCheckGaussDBMysqlInstanceId(t *testing.T) { } } +// lintignore:AT003 +func TestAccPreCheckGaussDBMysqlNodeId(t *testing.T) { + if HW_GAUSSDB_MYSQL_NODE_ID == "" { + t.Skip("HW_GAUSSDB_MYSQL_NODE_ID must be set for GaussDB MySQL acceptance tests.") + } +} + // lintignore:AT003 func TestAccPreCheckGaussDBMysqlDatabaseName(t *testing.T) { if HW_GAUSSDB_MYSQL_DATABASE_NAME == "" { @@ -1247,6 +1257,13 @@ func TestAccPreCheckGaussDBMysqlJobId(t *testing.T) { } } +// lintignore:AT003 +func TestAccPreCheckGaussDBMysqlTimeRange(t *testing.T) { + if HW_GAUSSDB_MYSQL_START_TIME == "" || HW_GAUSSDB_MYSQL_END_TIME == "" { + t.Skip("HW_GAUSSDB_MYSQL_START_TIME and HW_GAUSSDB_MYSQL_END_TIME must be set for GaussDB MySQL acceptance tests") + } +} + // lintignore:AT003 func TestAccPreCheckVODWatermark(t *testing.T) { if HW_VOD_WATERMARK_FILE == "" { diff --git a/huaweicloud/services/acceptance/gaussdb/data_source_huaweicloud_gaussdb_mysql_slow_logs_test.go b/huaweicloud/services/acceptance/gaussdb/data_source_huaweicloud_gaussdb_mysql_slow_logs_test.go new file mode 100644 index 0000000000..fac0d49041 --- /dev/null +++ b/huaweicloud/services/acceptance/gaussdb/data_source_huaweicloud_gaussdb_mysql_slow_logs_test.go @@ -0,0 +1,93 @@ +package gaussdb + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + + "github.com/huaweicloud/terraform-provider-huaweicloud/huaweicloud/services/acceptance" +) + +func TestAccDataSourceGaussDBMysqlSlowLogs_basic(t *testing.T) { + dataSource := "data.huaweicloud_gaussdb_mysql_slow_logs.test" + dc := acceptance.InitDataSourceCheck(dataSource) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + acceptance.TestAccPreCheck(t) + acceptance.TestAccPreCheckGaussDBMysqlInstanceId(t) + acceptance.TestAccPreCheckGaussDBMysqlNodeId(t) + acceptance.TestAccPreCheckGaussDBMysqlTimeRange(t) + }, + ProviderFactories: acceptance.TestAccProviderFactories, + Steps: []resource.TestStep{ + { + Config: testDataSourceGaussDBMysqlSlowLogs_basic(), + Check: resource.ComposeTestCheckFunc( + dc.CheckResourceExists(), + resource.TestCheckResourceAttrSet(dataSource, "slow_log_list.#"), + resource.TestCheckResourceAttrSet(dataSource, "slow_log_list.0.node_id"), + resource.TestCheckResourceAttrSet(dataSource, "slow_log_list.0.count"), + resource.TestCheckResourceAttrSet(dataSource, "slow_log_list.0.time"), + resource.TestCheckResourceAttrSet(dataSource, "slow_log_list.0.lock_time"), + resource.TestCheckResourceAttrSet(dataSource, "slow_log_list.0.rows_sent"), + resource.TestCheckResourceAttrSet(dataSource, "slow_log_list.0.rows_examined"), + resource.TestCheckResourceAttrSet(dataSource, "slow_log_list.0.database"), + resource.TestCheckResourceAttrSet(dataSource, "slow_log_list.0.users"), + resource.TestCheckResourceAttrSet(dataSource, "slow_log_list.0.query_sample"), + resource.TestCheckResourceAttrSet(dataSource, "slow_log_list.0.type"), + resource.TestCheckResourceAttrSet(dataSource, "slow_log_list.0.start_time"), + resource.TestCheckResourceAttrSet(dataSource, "slow_log_list.0.client_ip"), + + resource.TestCheckOutput("operate_type_filter_is_useful", "true"), + resource.TestCheckOutput("database_filter_is_useful", "true"), + ), + }, + }, + }) +} + +func testDataSourceGaussDBMysqlSlowLogs_basic() string { + return fmt.Sprintf(` +data "huaweicloud_gaussdb_mysql_slow_logs" "test" { + instance_id = "%[1]s" + node_id = "%[2]s" + start_time = "%[3]s" + end_time = "%[4]s" +} + +locals { + operate_type = "UPDATE" +} +data "huaweicloud_gaussdb_mysql_slow_logs" "operate_type_filter" { + instance_id = "%[1]s" + node_id = "%[2]s" + start_time = "%[3]s" + end_time = "%[4]s" + operate_type = "UPDATE" +} +output "operate_type_filter_is_useful" { + value = length(data.huaweicloud_gaussdb_mysql_slow_logs.operate_type_filter.slow_log_list) > 0 && alltrue( + [for v in data.huaweicloud_gaussdb_mysql_slow_logs.operate_type_filter.slow_log_list[*].type : v == local.operate_type] + ) +} + +locals { + database = "test_db_1" +} +data "huaweicloud_gaussdb_mysql_slow_logs" "database_filter" { + instance_id = "%[1]s" + node_id = "%[2]s" + start_time = "%[3]s" + end_time = "%[4]s" + database = "test_db_1" +} +output "database_filter_is_useful" { + value = length(data.huaweicloud_gaussdb_mysql_slow_logs.database_filter.slow_log_list) > 0 && alltrue( + [for v in data.huaweicloud_gaussdb_mysql_slow_logs.database_filter.slow_log_list[*].database : v == local.database] + ) +} +`, acceptance.HW_GAUSSDB_MYSQL_INSTANCE_ID, acceptance.HW_GAUSSDB_MYSQL_NODE_ID, acceptance.HW_GAUSSDB_MYSQL_START_TIME, + acceptance.HW_GAUSSDB_MYSQL_END_TIME) +} diff --git a/huaweicloud/services/gaussdb/data_source_huaweicloud_gaussdb_mysql_slow_logs.go b/huaweicloud/services/gaussdb/data_source_huaweicloud_gaussdb_mysql_slow_logs.go new file mode 100644 index 0000000000..551df96f81 --- /dev/null +++ b/huaweicloud/services/gaussdb/data_source_huaweicloud_gaussdb_mysql_slow_logs.go @@ -0,0 +1,232 @@ +package gaussdb + +import ( + "context" + "strings" + + "github.com/hashicorp/go-multierror" + "github.com/hashicorp/go-uuid" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/chnsz/golangsdk" + + "github.com/huaweicloud/terraform-provider-huaweicloud/huaweicloud/config" + "github.com/huaweicloud/terraform-provider-huaweicloud/huaweicloud/utils" +) + +// @API GaussDBforMySQL POST /v3.1/{project_id}/instances/{instance_id}/slow-logs +func DataSourceGaussDBMysqlSlowLogs() *schema.Resource { + return &schema.Resource{ + ReadContext: dataSourceGaussDBMysqlSlowLogsRead, + + Schema: map[string]*schema.Schema{ + "region": { + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: `Specifies the region in which to query the resource.`, + }, + "instance_id": { + Type: schema.TypeString, + Required: true, + Description: `Specifies the ID of the GaussDB MySQL instance.`, + }, + "node_id": { + Type: schema.TypeString, + Required: true, + Description: `Specifies the ID of the instance node.`, + }, + "start_time": { + Type: schema.TypeString, + Required: true, + Description: `Specifies the start time in the **yyyy-mm-ddThh:mm:ssZ** format.`, + }, + "end_time": { + Type: schema.TypeString, + Required: true, + Description: `Specifies the end time in the **yyyy-mm-ddThh:mm:ssZ** format.`, + }, + "operate_type": { + Type: schema.TypeString, + Optional: true, + Description: `Specifies the SQL statement type.`, + }, + "database": { + Type: schema.TypeString, + Optional: true, + Description: `Specifies the database that slow query logs belong to.`, + }, + "slow_log_list": { + Type: schema.TypeList, + Computed: true, + Description: `Indicates the list of the slow logs.`, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "node_id": { + Type: schema.TypeString, + Computed: true, + Description: `Indicates the ID of the instance node.`, + }, + "count": { + Type: schema.TypeString, + Computed: true, + Description: `Indicates the number of executions.`, + }, + "time": { + Type: schema.TypeString, + Computed: true, + Description: `Indicates the execution time.`, + }, + "lock_time": { + Type: schema.TypeString, + Computed: true, + Description: `Indicates the lock wait time.`, + }, + "rows_sent": { + Type: schema.TypeString, + Computed: true, + Description: `Indicates the number of sent rows.`, + }, + "rows_examined": { + Type: schema.TypeString, + Computed: true, + Description: `Indicates the number of scanned rows.`, + }, + "database": { + Type: schema.TypeString, + Computed: true, + Description: `Indicates the database that slow query logs belong to.`, + }, + "users": { + Type: schema.TypeString, + Computed: true, + Description: `Indicates the name of the account.`, + }, + "query_sample": { + Type: schema.TypeString, + Computed: true, + Description: `Indicates the execution syntax.`, + }, + "type": { + Type: schema.TypeString, + Computed: true, + Description: `Indicates the statement type.`, + }, + "start_time": { + Type: schema.TypeString, + Computed: true, + Description: `Indicates the start time in the **yyyy-mm-ddThh:mm:ssZ** format.`, + }, + "client_ip": { + Type: schema.TypeString, + Computed: true, + Description: `Indicates the IP address of the client.`, + }, + }, + }, + }, + }, + } +} + +func dataSourceGaussDBMysqlSlowLogsRead(_ context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + cfg := meta.(*config.Config) + region := cfg.GetRegion(d) + + var mErr *multierror.Error + + var ( + httpUrl = "v3.1/{project_id}/instances/{instance_id}/slow-logs" + product = "gaussdb" + ) + client, err := cfg.NewServiceClient(product, region) + if err != nil { + return diag.Errorf("error creating GaussDB Client: %s", err) + } + + getPath := client.Endpoint + httpUrl + getPath = strings.ReplaceAll(getPath, "{project_id}", client.ProjectID) + getPath = strings.ReplaceAll(getPath, "{instance_id}", d.Get("instance_id").(string)) + + getOpt := golangsdk.RequestOpts{ + KeepResponseBody: true, + } + + lineNum := "" + limit := 100 + res := make([]map[string]interface{}, 0) + for { + getOpt.JSONBody = utils.RemoveNil(buildGetSlowLogsParams(d, lineNum, limit)) + getResp, err := client.Request("POST", getPath, &getOpt) + if err != nil { + return diag.Errorf("error retrieving GaussDB MySQL slow logs: %s", err) + } + + getRespBody, err := utils.FlattenResponse(getResp) + if err != nil { + return diag.FromErr(err) + } + slowLogs, nextLineNum := flattenGaussDBMysqlSlowLogs(getRespBody) + res = append(res, slowLogs...) + if len(slowLogs) < limit { + break + } + lineNum = nextLineNum + } + + dataSourceId, err := uuid.GenerateUUID() + if err != nil { + return diag.Errorf("unable to generate ID: %s", err) + } + d.SetId(dataSourceId) + + mErr = multierror.Append( + mErr, + d.Set("region", region), + d.Set("slow_log_list", res), + ) + return diag.FromErr(mErr.ErrorOrNil()) +} + +func buildGetSlowLogsParams(d *schema.ResourceData, lineNum string, limit int) map[string]interface{} { + bodyParams := map[string]interface{}{ + "node_id": d.Get("node_id").(string), + "start_time": d.Get("start_time").(string), + "end_time": d.Get("end_time").(string), + "line_num": utils.ValueIgnoreEmpty(lineNum), + "limit": limit, + "operate_type": utils.ValueIgnoreEmpty(d.Get("operate_type").(string)), + "database": utils.ValueIgnoreEmpty(d.Get("database").(string)), + } + return bodyParams +} + +func flattenGaussDBMysqlSlowLogs(resp interface{}) ([]map[string]interface{}, string) { + slowLogsJson := utils.PathSearch("slow_log_list", resp, make([]interface{}, 0)) + slowLogsArray := slowLogsJson.([]interface{}) + if len(slowLogsArray) == 0 { + return nil, "" + } + + result := make([]map[string]interface{}, 0, len(slowLogsArray)) + var lineNum string + for _, slowLog := range slowLogsArray { + result = append(result, map[string]interface{}{ + "node_id": utils.PathSearch("node_id", slowLog, nil), + "count": utils.PathSearch("count", slowLog, nil), + "time": utils.PathSearch("time", slowLog, nil), + "lock_time": utils.PathSearch("lock_time", slowLog, nil), + "rows_sent": utils.PathSearch("rows_sent", slowLog, nil), + "rows_examined": utils.PathSearch("rows_examined", slowLog, nil), + "database": utils.PathSearch("database", slowLog, nil), + "users": utils.PathSearch("users", slowLog, nil), + "query_sample": utils.PathSearch("query_sample", slowLog, nil), + "type": utils.PathSearch("type", slowLog, nil), + "start_time": utils.PathSearch("start_time", slowLog, nil), + "client_ip": utils.PathSearch("client_ip", slowLog, nil), + }) + lineNum = utils.PathSearch("line_num", slowLog, "").(string) + } + return result, lineNum +}