1package clusters 2 3import ( 4 "fmt" 5 "net/http" 6 7 "github.com/gophercloud/gophercloud" 8 "github.com/gophercloud/gophercloud/pagination" 9) 10 11// AdjustmentType represents valid values for resizing a cluster. 12type AdjustmentType string 13 14const ( 15 ExactCapacityAdjustment AdjustmentType = "EXACT_CAPACITY" 16 ChangeInCapacityAdjustment AdjustmentType = "CHANGE_IN_CAPACITY" 17 ChangeInPercentageAdjustment AdjustmentType = "CHANGE_IN_PERCENTAGE" 18) 19 20// RecoveryAction represents valid values for recovering a cluster. 21type RecoveryAction string 22 23const ( 24 RebootRecovery RecoveryAction = "REBOOT" 25 RebuildRecovery RecoveryAction = "REBUILD" 26 RecreateRecovery RecoveryAction = "RECREATE" 27) 28 29// CreateOptsBuilder allows extensions to add additional parameters 30// to the Create request. 31type CreateOptsBuilder interface { 32 ToClusterCreateMap() (map[string]interface{}, error) 33} 34 35// CreateOpts represents options used to create a cluster. 36type CreateOpts struct { 37 Name string `json:"name" required:"true"` 38 DesiredCapacity int `json:"desired_capacity"` 39 ProfileID string `json:"profile_id" required:"true"` 40 MinSize *int `json:"min_size,omitempty"` 41 Timeout int `json:"timeout,omitempty"` 42 MaxSize int `json:"max_size,omitempty"` 43 Metadata map[string]interface{} `json:"metadata,omitempty"` 44 Config map[string]interface{} `json:"config,omitempty"` 45} 46 47// ToClusterCreateMap constructs a request body from CreateOpts. 48func (opts CreateOpts) ToClusterCreateMap() (map[string]interface{}, error) { 49 return gophercloud.BuildRequestBody(opts, "cluster") 50} 51 52// Create requests the creation of a new cluster. 53func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { 54 b, err := opts.ToClusterCreateMap() 55 if err != nil { 56 r.Err = err 57 return 58 } 59 var result *http.Response 60 result, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ 61 OkCodes: []int{200, 201, 202}, 62 }) 63 64 if r.Err == nil { 65 r.Header = result.Header 66 } 67 68 return 69} 70 71// Get retrieves details of a single cluster. 72func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { 73 var result *http.Response 74 result, r.Err = client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{ 75 OkCodes: []int{200}, 76 }) 77 78 if r.Err == nil { 79 r.Header = result.Header 80 } 81 82 return 83} 84 85// ListOptsBuilder allows extensions to add additional parameters to 86// the List request. 87type ListOptsBuilder interface { 88 ToClusterListQuery() (string, error) 89} 90 91// ListOpts represents options to list clusters. 92type ListOpts struct { 93 Limit int `q:"limit"` 94 Marker string `q:"marker"` 95 Sort string `q:"sort"` 96 GlobalProject *bool `q:"global_project"` 97 Name string `q:"name,omitempty"` 98 Status string `q:"status,omitempty"` 99} 100 101// ToClusterListQuery formats a ListOpts into a query string. 102func (opts ListOpts) ToClusterListQuery() (string, error) { 103 q, err := gophercloud.BuildQueryString(opts) 104 return q.String(), err 105} 106 107// List instructs OpenStack to provide a list of clusters. 108func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { 109 url := listURL(client) 110 if opts != nil { 111 query, err := opts.ToClusterListQuery() 112 if err != nil { 113 return pagination.Pager{Err: err} 114 } 115 url += query 116 } 117 118 return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { 119 return ClusterPage{pagination.LinkedPageBase{PageResult: r}} 120 }) 121} 122 123// UpdateOptsBuilder allows extensions to add additional parameters to the 124// Update request. 125type UpdateOptsBuilder interface { 126 ToClusterUpdateMap() (map[string]interface{}, error) 127} 128 129// UpdateOpts represents options to update a cluster. 130type UpdateOpts struct { 131 Config string `json:"config,omitempty"` 132 Name string `json:"name,omitempty"` 133 ProfileID string `json:"profile_id,omitempty"` 134 Timeout *int `json:"timeout,omitempty"` 135 Metadata map[string]interface{} `json:"metadata,omitempty"` 136 ProfileOnly *bool `json:"profile_only,omitempty"` 137} 138 139// ToClusterUpdateMap assembles a request body based on the contents of 140// UpdateOpts. 141func (opts UpdateOpts) ToClusterUpdateMap() (map[string]interface{}, error) { 142 b, err := gophercloud.BuildRequestBody(opts, "cluster") 143 if err != nil { 144 return nil, err 145 } 146 return b, nil 147} 148 149// Update will update an existing cluster. 150func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { 151 b, err := opts.ToClusterUpdateMap() 152 if err != nil { 153 r.Err = err 154 return r 155 } 156 157 var result *http.Response 158 result, r.Err = client.Patch(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ 159 OkCodes: []int{200, 202}, 160 }) 161 162 if r.Err == nil { 163 r.Header = result.Header 164 } 165 return 166} 167 168// Delete deletes the specified cluster ID. 169func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { 170 var result *http.Response 171 result, r.Err = client.Delete(deleteURL(client, id), nil) 172 if r.Err == nil { 173 r.Header = result.Header 174 } 175 return 176} 177 178// ResizeOptsBuilder allows extensions to add additional parameters to the 179// resize request. 180type ResizeOptsBuilder interface { 181 ToClusterResizeMap() (map[string]interface{}, error) 182} 183 184// ResizeOpts represents options for resizing a cluster. 185type ResizeOpts struct { 186 AdjustmentType AdjustmentType `json:"adjustment_type,omitempty"` 187 Number interface{} `json:"number,omitempty"` 188 MinSize *int `json:"min_size,omitempty"` 189 MaxSize *int `json:"max_size,omitempty"` 190 MinStep *int `json:"min_step,omitempty"` 191 Strict *bool `json:"strict,omitempty"` 192} 193 194// ToClusterResizeMap constructs a request body from ResizeOpts. 195func (opts ResizeOpts) ToClusterResizeMap() (map[string]interface{}, error) { 196 if opts.AdjustmentType != "" && opts.Number == nil { 197 return nil, fmt.Errorf("Number field MUST NOT be empty when AdjustmentType field used") 198 } 199 200 switch opts.Number.(type) { 201 case nil, int, int32, int64: 202 // Valid type. Always allow 203 case float32, float64: 204 if opts.AdjustmentType != ChangeInPercentageAdjustment { 205 return nil, fmt.Errorf("Only ChangeInPercentageAdjustment allows float value for Number field") 206 } 207 default: 208 return nil, fmt.Errorf("Number field must be either int, float, or omitted") 209 } 210 211 return gophercloud.BuildRequestBody(opts, "resize") 212} 213 214func Resize(client *gophercloud.ServiceClient, id string, opts ResizeOptsBuilder) (r ActionResult) { 215 b, err := opts.ToClusterResizeMap() 216 if err != nil { 217 r.Err = err 218 return 219 } 220 221 var result *http.Response 222 result, r.Err = client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ 223 OkCodes: []int{200, 201, 202}, 224 }) 225 if r.Err == nil { 226 r.Header = result.Header 227 } 228 229 return 230} 231 232// ScaleInOptsBuilder allows extensions to add additional parameters to the 233// ScaleIn request. 234type ScaleInOptsBuilder interface { 235 ToClusterScaleInMap() (map[string]interface{}, error) 236} 237 238// ScaleInOpts represents options used to scale-in a cluster. 239type ScaleInOpts struct { 240 Count *int `json:"count,omitempty"` 241} 242 243// ToClusterScaleInMap constructs a request body from ScaleInOpts. 244func (opts ScaleInOpts) ToClusterScaleInMap() (map[string]interface{}, error) { 245 return gophercloud.BuildRequestBody(opts, "scale_in") 246} 247 248// ScaleIn will reduce the capacity of a cluster. 249func ScaleIn(client *gophercloud.ServiceClient, id string, opts ScaleInOptsBuilder) (r ActionResult) { 250 b, err := opts.ToClusterScaleInMap() 251 if err != nil { 252 r.Err = err 253 return 254 } 255 var result *http.Response 256 result, r.Err = client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ 257 OkCodes: []int{200, 201, 202}, 258 }) 259 if r.Err == nil { 260 r.Header = result.Header 261 } 262 263 return 264} 265 266// ScaleOutOptsBuilder allows extensions to add additional parameters to the 267// ScaleOut request. 268type ScaleOutOptsBuilder interface { 269 ToClusterScaleOutMap() (map[string]interface{}, error) 270} 271 272// ScaleOutOpts represents options used to scale-out a cluster. 273type ScaleOutOpts struct { 274 Count int `json:"count,omitempty"` 275} 276 277// ToClusterScaleOutMap constructs a request body from ScaleOutOpts. 278func (opts ScaleOutOpts) ToClusterScaleOutMap() (map[string]interface{}, error) { 279 return gophercloud.BuildRequestBody(opts, "scale_out") 280} 281 282// ScaleOut will increase the capacity of a cluster. 283func ScaleOut(client *gophercloud.ServiceClient, id string, opts ScaleOutOptsBuilder) (r ActionResult) { 284 b, err := opts.ToClusterScaleOutMap() 285 if err != nil { 286 r.Err = err 287 return 288 } 289 var result *http.Response 290 result, r.Err = client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ 291 OkCodes: []int{200, 201, 202}, 292 }) 293 if r.Err == nil { 294 r.Header = result.Header 295 } 296 297 return 298} 299 300// AttachPolicyOptsBuilder allows extensions to add additional parameters to the 301// AttachPolicy request. 302type AttachPolicyOptsBuilder interface { 303 ToClusterAttachPolicyMap() (map[string]interface{}, error) 304} 305 306// PolicyOpts params 307type AttachPolicyOpts struct { 308 PolicyID string `json:"policy_id" required:"true"` 309 Enabled *bool `json:"enabled,omitempty"` 310} 311 312// ToClusterAttachPolicyMap constructs a request body from AttachPolicyOpts. 313func (opts AttachPolicyOpts) ToClusterAttachPolicyMap() (map[string]interface{}, error) { 314 return gophercloud.BuildRequestBody(opts, "policy_attach") 315} 316 317// Attach Policy will attach a policy to a cluster. 318func AttachPolicy(client *gophercloud.ServiceClient, id string, opts AttachPolicyOptsBuilder) (r ActionResult) { 319 b, err := opts.ToClusterAttachPolicyMap() 320 if err != nil { 321 r.Err = err 322 return 323 } 324 var result *http.Response 325 result, r.Err = client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ 326 OkCodes: []int{200, 201, 202}, 327 }) 328 if r.Err == nil { 329 r.Header = result.Header 330 } 331 332 return 333} 334 335// UpdatePolicyOptsBuilder allows extensions to add additional parameters to the 336// UpdatePolicy request. 337type UpdatePolicyOptsBuilder interface { 338 ToClusterUpdatePolicyMap() (map[string]interface{}, error) 339} 340 341// UpdatePolicyOpts represents options used to update a cluster policy. 342type UpdatePolicyOpts struct { 343 PolicyID string `json:"policy_id" required:"true"` 344 Enabled *bool `json:"enabled,omitempty" required:"true"` 345} 346 347// ToClusterUpdatePolicyMap constructs a request body from UpdatePolicyOpts. 348func (opts UpdatePolicyOpts) ToClusterUpdatePolicyMap() (map[string]interface{}, error) { 349 return gophercloud.BuildRequestBody(opts, "policy_update") 350} 351 352// UpdatePolicy will update a cluster's policy. 353func UpdatePolicy(client *gophercloud.ServiceClient, id string, opts UpdatePolicyOptsBuilder) (r ActionResult) { 354 b, err := opts.ToClusterUpdatePolicyMap() 355 if err != nil { 356 r.Err = err 357 return 358 } 359 var result *http.Response 360 result, r.Err = client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ 361 OkCodes: []int{200, 201, 202}, 362 }) 363 if r.Err == nil { 364 r.Header = result.Header 365 } 366 367 return 368} 369 370// DetachPolicyOptsBuilder allows extensions to add additional parameters to the 371// DetachPolicy request. 372type DetachPolicyOptsBuilder interface { 373 ToClusterDetachPolicyMap() (map[string]interface{}, error) 374} 375 376// DetachPolicyOpts represents options used to detach a policy from a cluster. 377type DetachPolicyOpts struct { 378 PolicyID string `json:"policy_id" required:"true"` 379} 380 381// ToClusterDetachPolicyMap constructs a request body from DetachPolicyOpts. 382func (opts DetachPolicyOpts) ToClusterDetachPolicyMap() (map[string]interface{}, error) { 383 return gophercloud.BuildRequestBody(opts, "policy_detach") 384} 385 386// DetachPolicy will detach a policy from a cluster. 387func DetachPolicy(client *gophercloud.ServiceClient, id string, opts DetachPolicyOptsBuilder) (r ActionResult) { 388 b, err := opts.ToClusterDetachPolicyMap() 389 if err != nil { 390 r.Err = err 391 return 392 } 393 394 var result *http.Response 395 result, r.Err = client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ 396 OkCodes: []int{200, 201, 202}, 397 }) 398 if r.Err == nil { 399 r.Header = result.Header 400 } 401 402 return 403} 404 405// ListPolicyOptsBuilder allows extensions to add additional parameters to the 406// ListPolicies request. 407type ListPoliciesOptsBuilder interface { 408 ToClusterPoliciesListQuery() (string, error) 409} 410 411// ListPoliciesOpts represents options to list a cluster's policies. 412type ListPoliciesOpts struct { 413 Enabled *bool `q:"enabled"` 414 Name string `q:"policy_name"` 415 Type string `q:"policy_type"` 416 Sort string `q:"sort"` 417} 418 419// ToClusterPoliciesListQuery formats a ListOpts into a query string. 420func (opts ListPoliciesOpts) ToClusterPoliciesListQuery() (string, error) { 421 q, err := gophercloud.BuildQueryString(opts) 422 return q.String(), err 423} 424 425// ListPolicies instructs OpenStack to provide a list of policies for a cluster. 426func ListPolicies(client *gophercloud.ServiceClient, clusterID string, opts ListPoliciesOptsBuilder) pagination.Pager { 427 url := listPoliciesURL(client, clusterID) 428 if opts != nil { 429 query, err := opts.ToClusterPoliciesListQuery() 430 if err != nil { 431 return pagination.Pager{Err: err} 432 } 433 url += query 434 } 435 436 return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { 437 return ClusterPolicyPage{pagination.SinglePageBase(r)} 438 }) 439} 440 441// GetPolicy retrieves details of a cluster policy. 442func GetPolicy(client *gophercloud.ServiceClient, clusterID string, policyID string) (r GetPolicyResult) { 443 _, r.Err = client.Get(getPolicyURL(client, clusterID, policyID), &r.Body, nil) 444 return 445} 446 447// RecoverOptsBuilder allows extensions to add additional parameters to the 448// Recover request. 449type RecoverOptsBuilder interface { 450 ToClusterRecoverMap() (map[string]interface{}, error) 451} 452 453// RecoverOpts represents options used to recover a cluster. 454type RecoverOpts struct { 455 Operation RecoveryAction `json:"operation,omitempty"` 456 Check *bool `json:"check,omitempty"` 457 CheckCapacity *bool `json:"check_capacity,omitempty"` 458} 459 460// ToClusterRecovermap constructs a request body from RecoverOpts. 461func (opts RecoverOpts) ToClusterRecoverMap() (map[string]interface{}, error) { 462 return gophercloud.BuildRequestBody(opts, "recover") 463} 464 465// Recover implements cluster recover request. 466func Recover(client *gophercloud.ServiceClient, id string, opts RecoverOptsBuilder) (r ActionResult) { 467 b, err := opts.ToClusterRecoverMap() 468 if err != nil { 469 r.Err = err 470 return 471 } 472 var result *http.Response 473 result, r.Err = client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ 474 OkCodes: []int{200, 201, 202}, 475 }) 476 if r.Err == nil { 477 r.Header = result.Header 478 } 479 480 return 481} 482 483// Check will perform a health check on a cluster. 484func Check(client *gophercloud.ServiceClient, id string) (r ActionResult) { 485 b := map[string]interface{}{ 486 "check": map[string]interface{}{}, 487 } 488 489 var result *http.Response 490 result, r.Err = client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ 491 OkCodes: []int{200, 201, 202}, 492 }) 493 if r.Err == nil { 494 r.Header = result.Header 495 } 496 497 return 498} 499 500// ToClusterCompleteLifecycleMap constructs a request body from CompleteLifecycleOpts. 501func (opts CompleteLifecycleOpts) ToClusterCompleteLifecycleMap() (map[string]interface{}, error) { 502 return gophercloud.BuildRequestBody(opts, "complete_lifecycle") 503} 504 505type CompleteLifecycleOpts struct { 506 LifecycleActionTokenID string `json:"lifecycle_action_token" required:"true"` 507} 508 509func CompleteLifecycle(client *gophercloud.ServiceClient, id string, opts CompleteLifecycleOpts) (r ActionResult) { 510 b, err := opts.ToClusterCompleteLifecycleMap() 511 if err != nil { 512 r.Err = err 513 return 514 } 515 516 var result *http.Response 517 result, r.Err = client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ 518 OkCodes: []int{202}, 519 }) 520 if r.Err == nil { 521 r.Header = result.Header 522 } 523 524 return 525} 526 527func (opts AddNodesOpts) ToClusterAddNodeMap() (map[string]interface{}, error) { 528 return gophercloud.BuildRequestBody(opts, "add_nodes") 529} 530 531type AddNodesOpts struct { 532 Nodes []string `json:"nodes" required:"true"` 533} 534 535func AddNodes(client *gophercloud.ServiceClient, id string, opts AddNodesOpts) (r ActionResult) { 536 b, err := opts.ToClusterAddNodeMap() 537 if err != nil { 538 r.Err = err 539 return 540 } 541 var result *http.Response 542 result, r.Err = client.Post(nodeURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ 543 OkCodes: []int{202}, 544 }) 545 r.Header = result.Header 546 return 547} 548 549func (opts RemoveNodesOpts) ToClusterRemoveNodeMap() (map[string]interface{}, error) { 550 return gophercloud.BuildRequestBody(opts, "del_nodes") 551} 552 553type RemoveNodesOpts struct { 554 Nodes []string `json:"nodes" required:"true"` 555} 556 557func RemoveNodes(client *gophercloud.ServiceClient, clusterID string, opts RemoveNodesOpts) (r DeleteResult) { 558 b, err := opts.ToClusterRemoveNodeMap() 559 if err != nil { 560 r.Err = err 561 return 562 } 563 var result *http.Response 564 result, r.Err = client.Post(nodeURL(client, clusterID), b, &r.Body, &gophercloud.RequestOpts{ 565 OkCodes: []int{202}, 566 }) 567 r.Header = result.Header 568 return 569} 570 571func (opts ReplaceNodesOpts) ToClusterReplaceNodeMap() (map[string]interface{}, error) { 572 return gophercloud.BuildRequestBody(opts, "replace_nodes") 573} 574 575type ReplaceNodesOpts struct { 576 Nodes map[string]string `json:"nodes" required:"true"` 577} 578 579func ReplaceNodes(client *gophercloud.ServiceClient, id string, opts ReplaceNodesOpts) (r ActionResult) { 580 b, err := opts.ToClusterReplaceNodeMap() 581 if err != nil { 582 r.Err = err 583 return 584 } 585 var result *http.Response 586 result, r.Err = client.Post(nodeURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ 587 OkCodes: []int{202}, 588 }) 589 r.Header = result.Header 590 return 591} 592 593type CollectOptsBuilder interface { 594 ToClusterCollectMap() (string, error) 595} 596 597// CollectOpts represents options to collect attribute values across a cluster 598type CollectOpts struct { 599 Path string `q:"path" required:"true"` 600} 601 602func (opts CollectOpts) ToClusterCollectMap() (string, error) { 603 return opts.Path, nil 604} 605 606// Collect instructs OpenStack to aggregate attribute values across a cluster 607func Collect(client *gophercloud.ServiceClient, id string, opts CollectOptsBuilder) (r CollectResult) { 608 query, err := opts.ToClusterCollectMap() 609 if err != nil { 610 r.Err = err 611 return 612 } 613 614 var result *http.Response 615 result, r.Err = client.Get(collectURL(client, id, query), &r.Body, &gophercloud.RequestOpts{ 616 OkCodes: []int{200}, 617 }) 618 619 if r.Err == nil { 620 r.Header = result.Header 621 } 622 623 return 624} 625 626// OperationName represents valid values for cluster operation 627type OperationName string 628 629const ( 630 // Nova Profile Op Names 631 RebootOperation OperationName = "reboot" 632 RebuildOperation OperationName = "rebuild" 633 ChangePasswordOperation OperationName = "change_password" 634 PauseOperation OperationName = "pause" 635 UnpauseOperation OperationName = "unpause" 636 SuspendOperation OperationName = "suspend" 637 ResumeOperation OperationName = "resume" 638 LockOperation OperationName = "lock" 639 UnlockOperation OperationName = "unlock" 640 StartOperation OperationName = "start" 641 StopOperation OperationName = "stop" 642 RescueOperation OperationName = "rescue" 643 UnrescueOperation OperationName = "unrescue" 644 EvacuateOperation OperationName = "evacuate" 645 646 // Heat Pofile Op Names 647 AbandonOperation OperationName = "abandon" 648) 649 650// ToClusterOperationMap constructs a request body from OperationOpts. 651func (opts OperationOpts) ToClusterOperationMap() (map[string]interface{}, error) { 652 operationArg := struct { 653 Filters OperationFilters `json:"filters,omitempty"` 654 Params OperationParams `json:"params,omitempty"` 655 }{ 656 Filters: opts.Filters, 657 Params: opts.Params, 658 } 659 660 return gophercloud.BuildRequestBody(operationArg, string(opts.Operation)) 661} 662 663// OperationOptsBuilder allows extensions to add additional parameters to the 664// Op request. 665type OperationOptsBuilder interface { 666 ToClusterOperationMap() (map[string]interface{}, error) 667} 668type OperationFilters map[string]interface{} 669type OperationParams map[string]interface{} 670 671// OperationOpts represents options used to perform an operation on a cluster 672type OperationOpts struct { 673 Operation OperationName `json:"operation" required:"true"` 674 Filters OperationFilters `json:"filters,omitempty"` 675 Params OperationParams `json:"params,omitempty"` 676} 677 678func Ops(client *gophercloud.ServiceClient, id string, opts OperationOptsBuilder) (r ActionResult) { 679 b, err := opts.ToClusterOperationMap() 680 if err != nil { 681 r.Err = err 682 return 683 } 684 685 var result *http.Response 686 result, r.Err = client.Post(opsURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ 687 OkCodes: []int{202}, 688 }) 689 if r.Err == nil { 690 r.Header = result.Header 691 } 692 693 return 694} 695