yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/aws/elasticache_instance.go (about) 1 // Copyright 2019 Yunion 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package aws 16 17 import ( 18 "strconv" 19 "strings" 20 "time" 21 22 "github.com/aws/aws-sdk-go/aws/awserr" 23 "github.com/aws/aws-sdk-go/service/elasticache" 24 25 "yunion.io/x/jsonutils" 26 "yunion.io/x/log" 27 "yunion.io/x/pkg/errors" 28 29 billingapi "yunion.io/x/cloudmux/pkg/apis/billing" 30 api "yunion.io/x/cloudmux/pkg/apis/compute" 31 "yunion.io/x/cloudmux/pkg/cloudprovider" 32 "yunion.io/x/cloudmux/pkg/multicloud" 33 "yunion.io/x/onecloud/pkg/util/billing" 34 ) 35 36 func (region *SRegion) DescribeElasticacheReplicationGroups(Id string) ([]*elasticache.ReplicationGroup, error) { 37 ecClient, err := region.getAwsElasticacheClient() 38 if err != nil { 39 return nil, errors.Wrap(err, "client.getAwsElasticacheClient") 40 } 41 42 input := elasticache.DescribeReplicationGroupsInput{} 43 44 replicaGroup := []*elasticache.ReplicationGroup{} 45 46 marker := "" 47 maxrecords := (int64)(100) 48 input.MaxRecords = &maxrecords 49 50 if len(Id) > 0 { 51 input.ReplicationGroupId = &Id 52 } 53 54 for { 55 if len(marker) >= 0 { 56 input.Marker = &marker 57 } 58 out, err := ecClient.DescribeReplicationGroups(&input) 59 if err != nil { 60 if e, ok := err.(awserr.Error); ok && e.Code() == "ReplicationGroupNotFoundFault" { 61 return nil, errors.Wrapf(cloudprovider.ErrNotFound, err.Error()) 62 } 63 return nil, errors.Wrap(err, "ecClient.DescribeReplicationGroups") 64 } 65 replicaGroup = append(replicaGroup, out.ReplicationGroups...) 66 67 if out.Marker != nil && len(*out.Marker) > 0 { 68 marker = *out.Marker 69 } else { 70 break 71 } 72 } 73 74 return replicaGroup, nil 75 } 76 77 func (region *SRegion) GetElasticaches() ([]SElasticache, error) { 78 replicaGroups, err := region.DescribeElasticacheReplicationGroups("") 79 if err != nil { 80 return nil, errors.Wrap(err, " region.DescribeElasticacheReplicationGroups") 81 } 82 result := []SElasticache{} 83 for i := range replicaGroups { 84 result = append(result, SElasticache{region: region, replicaGroup: replicaGroups[i]}) 85 } 86 return result, nil 87 } 88 89 func (region *SRegion) GetIElasticcaches() ([]cloudprovider.ICloudElasticcache, error) { 90 sElasticahes, err := region.GetElasticaches() 91 if err != nil { 92 return nil, errors.Wrap(err, "region.GetSElasticaches()") 93 } 94 result := []cloudprovider.ICloudElasticcache{} 95 for i := range sElasticahes { 96 result = append(result, &sElasticahes[i]) 97 } 98 return result, nil 99 } 100 101 func (region *SRegion) GetSElasticacheById(Id string) (*SElasticache, error) { 102 replicaGroups, err := region.DescribeElasticacheReplicationGroups(Id) 103 if err != nil { 104 return nil, errors.Wrap(err, " region.DescribeElasticacheReplicationGroups") 105 } 106 if len(replicaGroups) == 0 { 107 return nil, cloudprovider.ErrNotFound 108 } 109 if len(replicaGroups) > 1 { 110 return nil, cloudprovider.ErrDuplicateId 111 } 112 return &SElasticache{region: region, replicaGroup: replicaGroups[0]}, nil 113 } 114 115 func (region *SRegion) GetIElasticcacheById(id string) (cloudprovider.ICloudElasticcache, error) { 116 sElasticache, err := region.GetSElasticacheById(id) 117 if err != nil { 118 return nil, errors.Wrapf(err, "region.GetSElasticacheById(%s)", id) 119 } 120 return sElasticache, nil 121 } 122 123 func (region *SRegion) DescribeElasticacheClusters() ([]*elasticache.CacheCluster, error) { 124 ecClient, err := region.getAwsElasticacheClient() 125 if err != nil { 126 return nil, errors.Wrap(err, "client.getAwsElasticacheClient") 127 } 128 129 input := elasticache.DescribeCacheClustersInput{} 130 131 clusters := []*elasticache.CacheCluster{} 132 marker := "" 133 maxrecords := (int64)(100) 134 input.MaxRecords = &maxrecords 135 136 for { 137 if len(marker) >= 0 { 138 input.Marker = &marker 139 } 140 out, err := ecClient.DescribeCacheClusters(&input) 141 if err != nil { 142 return nil, errors.Wrap(err, "ecClient.DescribeCacheClusters") 143 } 144 clusters = append(clusters, out.CacheClusters...) 145 146 if out.Marker != nil && len(*out.Marker) > 0 { 147 marker = *out.Marker 148 } else { 149 break 150 } 151 } 152 153 return clusters, nil 154 } 155 156 func (region *SRegion) DescribeCacheSubnetGroups(Id string) ([]*elasticache.CacheSubnetGroup, error) { 157 ecClient, err := region.getAwsElasticacheClient() 158 if err != nil { 159 return nil, errors.Wrap(err, "client.getAwsElasticacheClient") 160 } 161 input := elasticache.DescribeCacheSubnetGroupsInput{} 162 subnetGroups := []*elasticache.CacheSubnetGroup{} 163 164 marker := "" 165 maxrecords := (int64)(100) 166 input.MaxRecords = &maxrecords 167 if len(Id) > 0 { 168 input.CacheSubnetGroupName = &Id 169 } 170 for { 171 if len(marker) >= 0 { 172 input.Marker = &marker 173 } 174 out, err := ecClient.DescribeCacheSubnetGroups(&input) 175 if err != nil { 176 return nil, errors.Wrap(err, "ecClient.DescribeCacheSubnetGroups") 177 } 178 subnetGroups = append(subnetGroups, out.CacheSubnetGroups...) 179 180 if out.Marker != nil && len(*out.Marker) > 0 { 181 marker = *out.Marker 182 } else { 183 break 184 } 185 } 186 187 return subnetGroups, nil 188 } 189 190 type SElasticache struct { 191 multicloud.SElasticcacheBase 192 AwsTags 193 194 region *SRegion 195 replicaGroup *elasticache.ReplicationGroup 196 cacheClusters []*elasticache.CacheCluster 197 subnetGroup *elasticache.CacheSubnetGroup 198 } 199 200 func (self *SElasticache) GetId() string { 201 return *self.replicaGroup.ReplicationGroupId 202 } 203 204 func (self *SElasticache) GetName() string { 205 return *self.replicaGroup.ReplicationGroupId 206 } 207 208 func (self *SElasticache) GetGlobalId() string { 209 return self.GetId() 210 } 211 212 func (self *SElasticache) GetStatus() string { 213 if self.replicaGroup.Status == nil { 214 return api.ELASTIC_CACHE_STATUS_UNKNOWN 215 } 216 // creating, available, modifying, deleting, create-failed, snapshotting 217 switch *self.replicaGroup.Status { 218 case "creating": 219 return api.ELASTIC_CACHE_STATUS_DEPLOYING 220 case "available": 221 return api.ELASTIC_CACHE_STATUS_RUNNING 222 case "modifying": 223 return api.ELASTIC_CACHE_STATUS_CHANGING 224 case "deleting": 225 return api.ELASTIC_CACHE_STATUS_DELETING 226 case "create-failed": 227 return api.ELASTIC_CACHE_STATUS_CREATE_FAILED 228 case "snapshotting": 229 return api.ELASTIC_CACHE_STATUS_SNAPSHOTTING 230 default: 231 return api.ELASTIC_CACHE_STATUS_UNKNOWN 232 } 233 } 234 235 func (self *SElasticache) Refresh() error { 236 if self.replicaGroup.ReplicationGroupId == nil { 237 return errors.Wrap(cloudprovider.ErrNotFound, "replicationGroupId not found") 238 } 239 replica, err := self.region.DescribeElasticacheReplicationGroups(*self.replicaGroup.ReplicationGroupId) 240 241 if err != nil { 242 return errors.Wrapf(err, "self.region.DescribeElasticacheReplicationGroups(%s)", *self.replicaGroup.ReplicationGroupId) 243 } 244 245 if len(replica) == 0 { 246 return cloudprovider.ErrNotFound 247 } 248 if len(replica) > 1 { 249 return cloudprovider.ErrDuplicateId 250 } 251 252 self.replicaGroup = replica[0] 253 self.cacheClusters = nil 254 self.subnetGroup = nil 255 256 return nil 257 } 258 259 func (self *SElasticache) GetBillingType() string { 260 return billingapi.BILLING_TYPE_POSTPAID 261 } 262 263 func (self *SElasticache) GetCreatedAt() time.Time { 264 return time.Time{} 265 } 266 267 func (self *SElasticache) GetExpiredAt() time.Time { 268 return time.Time{} 269 } 270 271 func (self *SElasticache) fetchClusters() error { 272 if self.cacheClusters != nil { 273 return nil 274 } 275 clusters, err := self.region.DescribeElasticacheClusters() 276 if err != nil { 277 return errors.Wrap(err, "self.region.DescribeElasticacheClusters") 278 } 279 self.cacheClusters = []*elasticache.CacheCluster{} 280 for i := range clusters { 281 if clusters[i].ReplicationGroupId != nil && *clusters[i].ReplicationGroupId == self.GetId() { 282 self.cacheClusters = append(self.cacheClusters, clusters[i]) 283 } 284 } 285 return nil 286 } 287 288 func (self *SElasticache) GetInstanceType() string { 289 if self.replicaGroup.CacheNodeType != nil { 290 return *self.replicaGroup.CacheNodeType 291 } 292 return "" 293 } 294 295 func (self *SElasticache) GetCapacityMB() int { 296 return 0 297 } 298 299 func (self *SElasticache) GetArchType() string { 300 if self.replicaGroup.ClusterEnabled != nil && *self.replicaGroup.ClusterEnabled == true { 301 return api.ELASTIC_CACHE_ARCH_TYPE_CLUSTER 302 } 303 304 self.fetchClusters() 305 if len(self.cacheClusters) > 1 { 306 return api.ELASTIC_CACHE_ARCH_TYPE_RWSPLIT 307 } 308 309 return api.ELASTIC_CACHE_ARCH_TYPE_SINGLE 310 } 311 312 func (self *SElasticache) GetNodeType() string { 313 nodeGroupNums := 1 314 for _, nodeGroup := range self.replicaGroup.NodeGroups { 315 if nodeGroup != nil { 316 nodeGroupNums = len(nodeGroup.NodeGroupMembers) 317 break 318 } 319 } 320 switch nodeGroupNums { 321 case 1: 322 return api.ELASTIC_CACHE_NODE_TYPE_SINGLE 323 case 2: 324 return api.ELASTIC_CACHE_NODE_TYPE_DOUBLE 325 case 3: 326 return api.ELASTIC_CACHE_NODE_TYPE_THREE 327 case 4: 328 return api.ELASTIC_CACHE_NODE_TYPE_FOUR 329 case 5: 330 return api.ELASTIC_CACHE_NODE_TYPE_FIVE 331 case 6: 332 return api.ELASTIC_CACHE_NODE_TYPE_SIX 333 } 334 335 return strconv.Itoa(nodeGroupNums) 336 } 337 338 func (self *SElasticache) GetEngine() string { 339 self.fetchClusters() 340 for _, cluster := range self.cacheClusters { 341 if cluster != nil && cluster.Engine != nil { 342 return *cluster.Engine 343 } 344 } 345 return "" 346 } 347 348 func (self *SElasticache) GetEngineVersion() string { 349 self.fetchClusters() 350 for _, cluster := range self.cacheClusters { 351 if cluster != nil && cluster.EngineVersion != nil { 352 return *cluster.EngineVersion 353 } 354 } 355 return "" 356 } 357 358 func (self *SElasticache) fetchSubnetGroup() error { 359 if self.subnetGroup != nil { 360 return nil 361 } 362 err := self.fetchClusters() 363 if err != nil { 364 return errors.Wrap(err, "self.fetchClusters()") 365 } 366 367 subnetGroupName := "" 368 for _, cluster := range self.cacheClusters { 369 if cluster != nil && cluster.CacheSubnetGroupName != nil { 370 subnetGroupName = *cluster.CacheSubnetGroupName 371 } 372 } 373 374 if len(subnetGroupName) == 0 { 375 return cloudprovider.ErrNotFound 376 } 377 378 subnetGroup, err := self.region.DescribeCacheSubnetGroups(subnetGroupName) 379 if err != nil { 380 return errors.Wrapf(err, "self.region.DescribeCacheSubnetGroups(%s)", subnetGroupName) 381 } 382 383 if len(subnetGroup) == 0 { 384 return errors.Wrapf(cloudprovider.ErrNotFound, "subnetGroup %s not found", subnetGroupName) 385 } 386 if len(subnetGroup) > 1 { 387 return cloudprovider.ErrDuplicateId 388 } 389 390 self.subnetGroup = subnetGroup[0] 391 return nil 392 } 393 394 func (self *SElasticache) GetVpcId() string { 395 err := self.fetchSubnetGroup() 396 if err != nil { 397 log.Errorf("Error:%s,self.fetchSubnetGroup()", err) 398 return "" 399 } 400 if self.subnetGroup != nil && self.subnetGroup.VpcId != nil { 401 return *self.subnetGroup.VpcId 402 } 403 return "" 404 } 405 406 func (self *SElasticache) GetZoneId() string { 407 return "" 408 } 409 410 func (self *SElasticache) GetNetworkType() string { 411 if len(self.GetVpcId()) > 0 { 412 return api.LB_NETWORK_TYPE_VPC 413 } 414 return api.LB_NETWORK_TYPE_CLASSIC 415 } 416 417 func (self *SElasticache) GetNetworkId() string { 418 return "" 419 } 420 421 // cluster mode(shard) 只有配置endpoint,no cluster mode 有primary/readonly endpoint 422 func (self *SElasticache) GetPrivateDNS() string { 423 for _, nodeGroup := range self.replicaGroup.NodeGroups { 424 if nodeGroup != nil && nodeGroup.PrimaryEndpoint != nil && nodeGroup.PrimaryEndpoint.Address != nil { 425 return *nodeGroup.PrimaryEndpoint.Address 426 } 427 } 428 return "" 429 } 430 431 func (self *SElasticache) GetPrivateIpAddr() string { 432 return "" 433 } 434 435 func (self *SElasticache) GetPrivateConnectPort() int { 436 for _, nodeGroup := range self.replicaGroup.NodeGroups { 437 if nodeGroup != nil && nodeGroup.PrimaryEndpoint != nil && nodeGroup.PrimaryEndpoint.Port != nil { 438 return int(*nodeGroup.PrimaryEndpoint.Port) 439 } 440 } 441 return 0 442 } 443 444 func (self *SElasticache) GetPublicDNS() string { 445 return "" 446 } 447 448 func (self *SElasticache) GetPublicIpAddr() string { 449 return "" 450 } 451 452 func (self *SElasticache) GetPublicConnectPort() int { 453 return 0 454 } 455 456 func (self *SElasticache) GetMaintainStartTime() string { 457 self.fetchClusters() 458 window := "" 459 for _, cluster := range self.cacheClusters { 460 if cluster != nil && cluster.PreferredMaintenanceWindow != nil { 461 window = *cluster.PreferredMaintenanceWindow 462 break 463 } 464 } 465 466 splited := strings.Split(window, "-") 467 return splited[0] 468 } 469 470 func (self *SElasticache) GetMaintainEndTime() string { 471 self.fetchClusters() 472 window := "" 473 for _, cluster := range self.cacheClusters { 474 if cluster != nil && cluster.PreferredMaintenanceWindow != nil { 475 window = *cluster.PreferredMaintenanceWindow 476 break 477 } 478 } 479 480 splited := strings.Split(window, "-") 481 if len(splited) == 2 { 482 return splited[1] 483 } 484 return "" 485 } 486 487 func (self *SElasticache) GetUsers() ([]SElasticacheUser, error) { 488 result := []SElasticacheUser{} 489 users, err := self.region.DescribeUsers("") 490 if err != nil { 491 return nil, errors.Wrap(err, "self.region.DescribeUsers") 492 } 493 for i := range users { 494 result = append(result, SElasticacheUser{region: self.region, user: users[i]}) 495 } 496 return result, nil 497 } 498 499 func (self *SElasticache) GetICloudElasticcacheAccounts() ([]cloudprovider.ICloudElasticcacheAccount, error) { 500 result := []cloudprovider.ICloudElasticcacheAccount{} 501 users, err := self.GetUsers() 502 if err != nil { 503 return nil, errors.Wrap(err, "self.GetUsers()") 504 } 505 for i := range users { 506 result = append(result, &users[i]) 507 } 508 return result, nil 509 } 510 511 func (self *SElasticache) GetUserById(id string) (*SElasticacheUser, error) { 512 users, err := self.region.DescribeUsers("") 513 if err != nil { 514 return nil, errors.Wrap(err, "self.region.DescribeUsers") 515 } 516 for i := range users { 517 temp := SElasticacheUser{region: self.region, user: users[i]} 518 if temp.GetId() == id { 519 return &temp, nil 520 } 521 } 522 return nil, cloudprovider.ErrNotFound 523 } 524 525 func (self *SElasticache) GetICloudElasticcacheAccount(accountId string) (cloudprovider.ICloudElasticcacheAccount, error) { 526 user, err := self.GetUserById(accountId) 527 if err != nil { 528 return nil, errors.Wrapf(err, "self.GetUserById(%s)", accountId) 529 } 530 return user, nil 531 } 532 533 func (self *SElasticache) GetICloudElasticcacheAcls() ([]cloudprovider.ICloudElasticcacheAcl, error) { 534 return []cloudprovider.ICloudElasticcacheAcl{}, nil 535 } 536 537 func (self *SElasticache) GetICloudElasticcacheAcl(aclId string) (cloudprovider.ICloudElasticcacheAcl, error) { 538 return nil, cloudprovider.ErrNotSupported 539 } 540 541 func (self *SElasticache) GetSnapshots() ([]SElasticacheSnapshop, error) { 542 result := []SElasticacheSnapshop{} 543 snapshots, err := self.region.DescribeSnapshots(self.GetName(), "") 544 if err != nil { 545 return nil, errors.Wrapf(err, " self.region.DescribeSnapshots(%s)", self.GetName()) 546 } 547 548 for i := range snapshots { 549 result = append(result, SElasticacheSnapshop{region: self.region, snapshot: snapshots[i]}) 550 } 551 return result, nil 552 } 553 554 func (self *SElasticache) GetICloudElasticcacheBackups() ([]cloudprovider.ICloudElasticcacheBackup, error) { 555 snapshots, err := self.GetSnapshots() 556 if err != nil { 557 return nil, errors.Wrap(err, "self.GetSnapshots()") 558 } 559 result := []cloudprovider.ICloudElasticcacheBackup{} 560 for i := range snapshots { 561 result = append(result, &snapshots[i]) 562 } 563 return result, nil 564 } 565 566 func (self *SElasticache) GetICloudElasticcacheBackup(backupId string) (cloudprovider.ICloudElasticcacheBackup, error) { 567 snapshots, err := self.GetSnapshots() 568 if err != nil { 569 return nil, errors.Wrap(err, "self.GetSnapshots()") 570 } 571 for i := range snapshots { 572 if snapshots[i].GetId() == backupId { 573 return &snapshots[i], nil 574 } 575 } 576 return nil, cloudprovider.ErrNotFound 577 } 578 579 func (self *SElasticache) GetParameterGroupName() (string, error) { 580 err := self.fetchClusters() 581 if err != nil { 582 return "", errors.Wrap(err, "self.fetchClusters()") 583 } 584 for _, cluster := range self.cacheClusters { 585 if cluster != nil && cluster.CacheParameterGroup != nil && cluster.CacheParameterGroup.CacheParameterGroupName != nil { 586 return *cluster.CacheParameterGroup.CacheParameterGroupName, nil 587 } 588 } 589 590 return "", cloudprovider.ErrNotFound 591 } 592 593 func (self *SElasticache) GetParameters() ([]SElasticacheParameter, error) { 594 groupName, err := self.GetParameterGroupName() 595 if err != nil { 596 return nil, errors.Wrap(err, "self.GetParameterGroupName()") 597 } 598 parameters, err := self.region.DescribeCacheParameters(groupName) 599 if err != nil { 600 return nil, errors.Wrapf(err, "self.region.DescribeCacheParameters(%s)", groupName) 601 } 602 603 result := []SElasticacheParameter{} 604 605 for i := range parameters { 606 result = append(result, SElasticacheParameter{parameterGroup: groupName, parameter: parameters[i]}) 607 } 608 return result, nil 609 } 610 611 func (self *SElasticache) GetICloudElasticcacheParameters() ([]cloudprovider.ICloudElasticcacheParameter, error) { 612 parameters, err := self.GetParameters() 613 if err != nil { 614 return nil, errors.Wrap(err, "self.GetParameters()") 615 } 616 result := []cloudprovider.ICloudElasticcacheParameter{} 617 for i := range parameters { 618 result = append(result, ¶meters[i]) 619 } 620 return result, nil 621 } 622 623 func (self *SElasticache) GetSecurityGroupIds() ([]string, error) { 624 err := self.fetchClusters() 625 if err != nil { 626 return nil, errors.Wrap(err, "self.fetchClusters()") 627 } 628 result := []string{} 629 for _, cluster := range self.cacheClusters { 630 if cluster != nil { 631 for _, securityGroup := range cluster.SecurityGroups { 632 if securityGroup != nil && securityGroup.Status != nil && *securityGroup.Status == "active" { 633 if securityGroup.SecurityGroupId != nil { 634 result = append(result, *securityGroup.SecurityGroupId) 635 } 636 } 637 } 638 } 639 } 640 641 return result, nil 642 } 643 644 func (self *SElasticache) AllocatePublicConnection(port int) (string, error) { 645 return "", cloudprovider.ErrNotSupported 646 } 647 648 func (self *SElasticache) ChangeInstanceSpec(spec string) error { 649 return cloudprovider.ErrNotSupported 650 } 651 652 func (self *SElasticache) CreateAccount(input cloudprovider.SCloudElasticCacheAccountInput) (cloudprovider.ICloudElasticcacheAccount, error) { 653 return nil, cloudprovider.ErrNotImplemented 654 } 655 656 func (self *SElasticache) CreateAcl(aclName, securityIps string) (cloudprovider.ICloudElasticcacheAcl, error) { 657 return nil, cloudprovider.ErrNotSupported 658 } 659 660 func (self *SElasticache) CreateBackup(desc string) (cloudprovider.ICloudElasticcacheBackup, error) { 661 return nil, cloudprovider.ErrNotImplemented 662 } 663 664 func (self *SElasticache) Delete() error { 665 return cloudprovider.ErrNotImplemented 666 } 667 668 func (self *SElasticache) FlushInstance(input cloudprovider.SCloudElasticCacheFlushInstanceInput) error { 669 return cloudprovider.ErrNotSupported 670 } 671 672 func (self *SElasticache) GetAuthMode() string { 673 return "" 674 } 675 676 func (self *SElasticache) ReleasePublicConnection() error { 677 return cloudprovider.ErrNotSupported 678 } 679 680 func (self *SElasticache) Renew(bc billing.SBillingCycle) error { 681 return cloudprovider.ErrNotSupported 682 } 683 684 func (self *SElasticache) Restart() error { 685 return cloudprovider.ErrNotSupported 686 } 687 688 func (self *SElasticache) SetMaintainTime(maintainStartTime, maintainEndTime string) error { 689 return cloudprovider.ErrNotImplemented 690 } 691 692 func (self *SElasticache) UpdateAuthMode(noPwdAccess bool, password string) error { 693 return cloudprovider.ErrNotImplemented 694 } 695 696 func (self *SElasticache) UpdateBackupPolicy(config cloudprovider.SCloudElasticCacheBackupPolicyUpdateInput) error { 697 return cloudprovider.ErrNotImplemented 698 } 699 700 func (self *SElasticache) UpdateInstanceParameters(config jsonutils.JSONObject) error { 701 return cloudprovider.ErrNotImplemented 702 } 703 704 func (self *SElasticache) UpdateSecurityGroups(secgroupIds []string) error { 705 return cloudprovider.ErrNotImplemented 706 }