~juju-qa/ubuntu/yakkety/juju/juju-1.25.8

« back to all changes in this revision

Viewing changes to src/gopkg.in/goose.v1/testservices/novaservice/service.go

  • Committer: Nicholas Skaggs
  • Date: 2016-12-02 17:28:37 UTC
  • Revision ID: nicholas.skaggs@canonical.com-20161202172837-jkrbdlyjcxtrii2n
Initial commit of 1.25.6

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Nova double testing service - internal direct API implementation
 
2
 
 
3
package novaservice
 
4
 
 
5
import (
 
6
        "fmt"
 
7
        "net/url"
 
8
        "regexp"
 
9
        "sort"
 
10
        "strings"
 
11
 
 
12
        "gopkg.in/goose.v1/nova"
 
13
        "gopkg.in/goose.v1/testservices"
 
14
        "gopkg.in/goose.v1/testservices/identityservice"
 
15
)
 
16
 
 
17
var _ testservices.HttpService = (*Nova)(nil)
 
18
var _ identityservice.ServiceProvider = (*Nova)(nil)
 
19
 
 
20
// Nova implements a OpenStack Nova testing service and
 
21
// contains the service double's internal state.
 
22
type Nova struct {
 
23
        testservices.ServiceInstance
 
24
        flavors                   map[string]nova.FlavorDetail
 
25
        servers                   map[string]nova.ServerDetail
 
26
        groups                    map[string]nova.SecurityGroup
 
27
        rules                     map[string]nova.SecurityGroupRule
 
28
        floatingIPs               map[string]nova.FloatingIP
 
29
        networks                  map[string]nova.Network
 
30
        serverGroups              map[string][]string
 
31
        serverIPs                 map[string][]string
 
32
        availabilityZones         map[string]nova.AvailabilityZone
 
33
        serverIdToAttachedVolumes map[string][]nova.VolumeAttachment
 
34
        nextServerId              int
 
35
        nextGroupId               int
 
36
        nextRuleId                int
 
37
        nextIPId                  int
 
38
        nextAttachmentId          int
 
39
}
 
40
 
 
41
func errorJSONEncode(err error) (int, string) {
 
42
        serverError, ok := err.(*testservices.ServerError)
 
43
        if !ok {
 
44
                serverError = testservices.NewInternalServerError(err.Error())
 
45
        }
 
46
        return serverError.Code(), serverError.AsJSON()
 
47
}
 
48
 
 
49
// endpoint returns either a versioned or non-versioned service
 
50
// endpoint URL from the given path.
 
51
func (n *Nova) endpointURL(version bool, path string) string {
 
52
        ep := n.Scheme + "://" + n.Hostname
 
53
        if version {
 
54
                ep += n.VersionPath + "/"
 
55
        }
 
56
        ep += n.TenantId
 
57
        if path != "" {
 
58
                ep += "/" + strings.TrimLeft(path, "/")
 
59
        }
 
60
        return ep
 
61
}
 
62
 
 
63
func (n *Nova) Endpoints() []identityservice.Endpoint {
 
64
        ep := identityservice.Endpoint{
 
65
                AdminURL:    n.endpointURL(true, ""),
 
66
                InternalURL: n.endpointURL(true, ""),
 
67
                PublicURL:   n.endpointURL(true, ""),
 
68
                Region:      n.Region,
 
69
        }
 
70
        return []identityservice.Endpoint{ep}
 
71
}
 
72
 
 
73
// New creates an instance of the Nova object, given the parameters.
 
74
func New(hostURL, versionPath, tenantId, region string, identityService identityservice.IdentityService) *Nova {
 
75
        URL, err := url.Parse(hostURL)
 
76
        if err != nil {
 
77
                panic(err)
 
78
        }
 
79
        hostname := URL.Host
 
80
        if !strings.HasSuffix(hostname, "/") {
 
81
                hostname += "/"
 
82
        }
 
83
        // Real openstack instances have flavours "out of the box". So we add some here.
 
84
        defaultFlavors := []nova.FlavorDetail{
 
85
                {Id: "1", Name: "m1.tiny", RAM: 512, VCPUs: 1},
 
86
                {Id: "2", Name: "m1.small", RAM: 2048, VCPUs: 1},
 
87
                {Id: "3", Name: "m1.medium", RAM: 4096, VCPUs: 2},
 
88
        }
 
89
        // Real openstack instances have a default security group "out of the box". So we add it here.
 
90
        defaultSecurityGroups := []nova.SecurityGroup{
 
91
                {Id: "999", Name: "default", Description: "default group"},
 
92
        }
 
93
        novaService := &Nova{
 
94
                flavors:                   make(map[string]nova.FlavorDetail),
 
95
                servers:                   make(map[string]nova.ServerDetail),
 
96
                groups:                    make(map[string]nova.SecurityGroup),
 
97
                rules:                     make(map[string]nova.SecurityGroupRule),
 
98
                floatingIPs:               make(map[string]nova.FloatingIP),
 
99
                networks:                  make(map[string]nova.Network),
 
100
                serverGroups:              make(map[string][]string),
 
101
                serverIPs:                 make(map[string][]string),
 
102
                availabilityZones:         make(map[string]nova.AvailabilityZone),
 
103
                serverIdToAttachedVolumes: make(map[string][]nova.VolumeAttachment),
 
104
                ServiceInstance: testservices.ServiceInstance{
 
105
                        IdentityService: identityService,
 
106
                        Scheme:          URL.Scheme,
 
107
                        Hostname:        hostname,
 
108
                        VersionPath:     versionPath,
 
109
                        TenantId:        tenantId,
 
110
                        Region:          region,
 
111
                },
 
112
        }
 
113
        if identityService != nil {
 
114
                identityService.RegisterServiceProvider("nova", "compute", novaService)
 
115
        }
 
116
        for i, flavor := range defaultFlavors {
 
117
                novaService.buildFlavorLinks(&flavor)
 
118
                defaultFlavors[i] = flavor
 
119
                err := novaService.addFlavor(flavor)
 
120
                if err != nil {
 
121
                        panic(err)
 
122
                }
 
123
        }
 
124
        for _, group := range defaultSecurityGroups {
 
125
                err := novaService.addSecurityGroup(group)
 
126
                if err != nil {
 
127
                        panic(err)
 
128
                }
 
129
        }
 
130
        // Add a sample default network
 
131
        var netId = "1"
 
132
        novaService.networks[netId] = nova.Network{
 
133
                Id:    netId,
 
134
                Label: "net",
 
135
                Cidr:  "10.0.0.0/24",
 
136
        }
 
137
        return novaService
 
138
}
 
139
 
 
140
// SetAvailabilityZones sets the availability zones for setting
 
141
// availability zones.
 
142
//
 
143
// Note: this is implemented as a public method rather than as
 
144
// an HTTP API for two reasons: availability zones are created
 
145
// indirectly via "host aggregates", which are a cloud-provider
 
146
// concept that we have not implemented, and because we want to
 
147
// be able to synthesize zone state changes.
 
148
func (n *Nova) SetAvailabilityZones(zones ...nova.AvailabilityZone) {
 
149
        n.availabilityZones = make(map[string]nova.AvailabilityZone)
 
150
        for _, z := range zones {
 
151
                n.availabilityZones[z.Name] = z
 
152
        }
 
153
}
 
154
 
 
155
// buildFlavorLinks populates the Links field of the passed
 
156
// FlavorDetail as needed by OpenStack HTTP API. Call this
 
157
// before addFlavor().
 
158
func (n *Nova) buildFlavorLinks(flavor *nova.FlavorDetail) {
 
159
        url := "/flavors/" + flavor.Id
 
160
        flavor.Links = []nova.Link{
 
161
                {Href: n.endpointURL(true, url), Rel: "self"},
 
162
                {Href: n.endpointURL(false, url), Rel: "bookmark"},
 
163
        }
 
164
}
 
165
 
 
166
// addFlavor creates a new flavor.
 
167
func (n *Nova) addFlavor(flavor nova.FlavorDetail) error {
 
168
        if err := n.ProcessFunctionHook(n, flavor); err != nil {
 
169
                return err
 
170
        }
 
171
        if _, err := n.flavor(flavor.Id); err == nil {
 
172
                return testservices.NewAddFlavorError(flavor.Id)
 
173
        }
 
174
        n.flavors[flavor.Id] = flavor
 
175
        return nil
 
176
}
 
177
 
 
178
// flavor retrieves an existing flavor by ID.
 
179
func (n *Nova) flavor(flavorId string) (*nova.FlavorDetail, error) {
 
180
        if err := n.ProcessFunctionHook(n, flavorId); err != nil {
 
181
                return nil, err
 
182
        }
 
183
        flavor, ok := n.flavors[flavorId]
 
184
        if !ok {
 
185
                return nil, testservices.NewNoSuchFlavorError(flavorId)
 
186
        }
 
187
        return &flavor, nil
 
188
}
 
189
 
 
190
// flavorAsEntity returns the stored FlavorDetail as Entity.
 
191
func (n *Nova) flavorAsEntity(flavorId string) (*nova.Entity, error) {
 
192
        if err := n.ProcessFunctionHook(n, flavorId); err != nil {
 
193
                return nil, err
 
194
        }
 
195
        flavor, err := n.flavor(flavorId)
 
196
        if err != nil {
 
197
                return nil, err
 
198
        }
 
199
        return &nova.Entity{
 
200
                Id:    flavor.Id,
 
201
                Name:  flavor.Name,
 
202
                Links: flavor.Links,
 
203
        }, nil
 
204
}
 
205
 
 
206
// allFlavors returns a list of all existing flavors.
 
207
func (n *Nova) allFlavors() []nova.FlavorDetail {
 
208
        var flavors []nova.FlavorDetail
 
209
        for _, flavor := range n.flavors {
 
210
                flavors = append(flavors, flavor)
 
211
        }
 
212
        return flavors
 
213
}
 
214
 
 
215
// allFlavorsAsEntities returns all flavors as Entity structs.
 
216
func (n *Nova) allFlavorsAsEntities() []nova.Entity {
 
217
        var entities []nova.Entity
 
218
        for _, flavor := range n.flavors {
 
219
                entities = append(entities, nova.Entity{
 
220
                        Id:    flavor.Id,
 
221
                        Name:  flavor.Name,
 
222
                        Links: flavor.Links,
 
223
                })
 
224
        }
 
225
        return entities
 
226
}
 
227
 
 
228
// removeFlavor deletes an existing flavor.
 
229
func (n *Nova) removeFlavor(flavorId string) error {
 
230
        if err := n.ProcessFunctionHook(n, flavorId); err != nil {
 
231
                return err
 
232
        }
 
233
        if _, err := n.flavor(flavorId); err != nil {
 
234
                return err
 
235
        }
 
236
        delete(n.flavors, flavorId)
 
237
        return nil
 
238
}
 
239
 
 
240
// buildServerLinks populates the Links field of the passed
 
241
// ServerDetail as needed by OpenStack HTTP API. Call this
 
242
// before addServer().
 
243
func (n *Nova) buildServerLinks(server *nova.ServerDetail) {
 
244
        url := "/servers/" + server.Id
 
245
        server.Links = []nova.Link{
 
246
                {Href: n.endpointURL(true, url), Rel: "self"},
 
247
                {Href: n.endpointURL(false, url), Rel: "bookmark"},
 
248
        }
 
249
}
 
250
 
 
251
// addServer creates a new server.
 
252
func (n *Nova) addServer(server nova.ServerDetail) error {
 
253
        if err := n.ProcessFunctionHook(n, &server); err != nil {
 
254
                return err
 
255
        }
 
256
        if _, err := n.server(server.Id); err == nil {
 
257
                return testservices.NewServerAlreadyExistsError(server.Id)
 
258
        }
 
259
        n.servers[server.Id] = server
 
260
        return nil
 
261
}
 
262
 
 
263
// server retrieves an existing server by ID.
 
264
func (n *Nova) server(serverId string) (*nova.ServerDetail, error) {
 
265
        if err := n.ProcessFunctionHook(n, serverId); err != nil {
 
266
                return nil, err
 
267
        }
 
268
        server, ok := n.servers[serverId]
 
269
        if !ok {
 
270
                return nil, testservices.NewServerByIDNotFoundError(serverId)
 
271
        }
 
272
        return &server, nil
 
273
}
 
274
 
 
275
// serverByName retrieves the first existing server with the given name.
 
276
func (n *Nova) serverByName(name string) (*nova.ServerDetail, error) {
 
277
        if err := n.ProcessFunctionHook(n, name); err != nil {
 
278
                return nil, err
 
279
        }
 
280
        for _, server := range n.servers {
 
281
                if server.Name == name {
 
282
                        return &server, nil
 
283
                }
 
284
        }
 
285
        return nil, testservices.NewServerByNameNotFoundError(name)
 
286
}
 
287
 
 
288
// serverAsEntity returns the stored ServerDetail as Entity.
 
289
func (n *Nova) serverAsEntity(serverId string) (*nova.Entity, error) {
 
290
        if err := n.ProcessFunctionHook(n, serverId); err != nil {
 
291
                return nil, err
 
292
        }
 
293
        server, err := n.server(serverId)
 
294
        if err != nil {
 
295
                return nil, err
 
296
        }
 
297
        return &nova.Entity{
 
298
                Id:    server.Id,
 
299
                UUID:  server.UUID,
 
300
                Name:  server.Name,
 
301
                Links: server.Links,
 
302
        }, nil
 
303
}
 
304
 
 
305
// filter is used internally by matchServers.
 
306
type filter map[string]string
 
307
 
 
308
// matchServers returns a list of matching servers, after applying the
 
309
// given filter. Each separate filter is combined with a logical AND.
 
310
// Each filter can have only one value. A nil filter matches all servers.
 
311
//
 
312
// This is tested to match OpenStack behavior. Regular expression
 
313
// matching is supported for FilterServer only, and the supported
 
314
// syntax is limited to whatever DB backend is used (see SQL
 
315
// REGEXP/RLIKE).
 
316
//
 
317
// Example:
 
318
//
 
319
// f := filter{
 
320
//     nova.FilterStatus: nova.StatusActive,
 
321
//     nova.FilterServer: `foo.*`,
 
322
// }
 
323
//
 
324
// This will match all servers with status "ACTIVE", and names starting
 
325
// with "foo".
 
326
func (n *Nova) matchServers(f filter) ([]nova.ServerDetail, error) {
 
327
        if err := n.ProcessFunctionHook(n, f); err != nil {
 
328
                return nil, err
 
329
        }
 
330
        var servers []nova.ServerDetail
 
331
        for _, server := range n.servers {
 
332
                servers = append(servers, server)
 
333
        }
 
334
        if len(f) == 0 {
 
335
                return servers, nil // empty filter matches everything
 
336
        }
 
337
        if status := f[nova.FilterStatus]; status != "" {
 
338
                matched := []nova.ServerDetail{}
 
339
                for _, server := range servers {
 
340
                        if server.Status == status {
 
341
                                matched = append(matched, server)
 
342
                        }
 
343
                }
 
344
                if len(matched) == 0 {
 
345
                        // no match, so no need to look further
 
346
                        return nil, nil
 
347
                }
 
348
                servers = matched
 
349
        }
 
350
        if nameRex := f[nova.FilterServer]; nameRex != "" {
 
351
                matched := []nova.ServerDetail{}
 
352
                rex, err := regexp.Compile(nameRex)
 
353
                if err != nil {
 
354
                        return nil, err
 
355
                }
 
356
                for _, server := range servers {
 
357
                        if rex.MatchString(server.Name) {
 
358
                                matched = append(matched, server)
 
359
                        }
 
360
                }
 
361
                if len(matched) == 0 {
 
362
                        // no match, here so ignore other results
 
363
                        return nil, nil
 
364
                }
 
365
                servers = matched
 
366
        }
 
367
        return servers, nil
 
368
        // TODO(dimitern) - 2013-02-11 bug=1121690
 
369
        // implement FilterFlavor, FilterImage, FilterMarker, FilterLimit and FilterChangesSince
 
370
}
 
371
 
 
372
// allServers returns a list of all existing servers.
 
373
// Filtering is supported, see filter type for more info.
 
374
func (n *Nova) allServers(f filter) ([]nova.ServerDetail, error) {
 
375
        return n.matchServers(f)
 
376
}
 
377
 
 
378
// allServersAsEntities returns all servers as Entity structs.
 
379
// Filtering is supported, see filter type for more info.
 
380
func (n *Nova) allServersAsEntities(f filter) ([]nova.Entity, error) {
 
381
        var entities []nova.Entity
 
382
        servers, err := n.matchServers(f)
 
383
        if err != nil {
 
384
                return nil, err
 
385
        }
 
386
        for _, server := range servers {
 
387
                entities = append(entities, nova.Entity{
 
388
                        Id:    server.Id,
 
389
                        UUID:  server.UUID,
 
390
                        Name:  server.Name,
 
391
                        Links: server.Links,
 
392
                })
 
393
        }
 
394
        return entities, nil
 
395
}
 
396
 
 
397
// removeServer deletes an existing server.
 
398
func (n *Nova) removeServer(serverId string) error {
 
399
        if err := n.ProcessFunctionHook(n, serverId); err != nil {
 
400
                return err
 
401
        }
 
402
        if _, err := n.server(serverId); err != nil {
 
403
                return err
 
404
        }
 
405
        delete(n.servers, serverId)
 
406
        return nil
 
407
}
 
408
 
 
409
// addSecurityGroup creates a new security group.
 
410
func (n *Nova) addSecurityGroup(group nova.SecurityGroup) error {
 
411
        if err := n.ProcessFunctionHook(n, group); err != nil {
 
412
                return err
 
413
        }
 
414
        if _, err := n.securityGroup(group.Id); err == nil {
 
415
                return testservices.NewSecurityGroupAlreadyExistsError(group.Id)
 
416
        }
 
417
        group.TenantId = n.TenantId
 
418
        if group.Rules == nil {
 
419
                group.Rules = []nova.SecurityGroupRule{}
 
420
        }
 
421
        n.groups[group.Id] = group
 
422
        return nil
 
423
}
 
424
 
 
425
// securityGroup retrieves an existing group by ID.
 
426
func (n *Nova) securityGroup(groupId string) (*nova.SecurityGroup, error) {
 
427
        if err := n.ProcessFunctionHook(n, groupId); err != nil {
 
428
                return nil, err
 
429
        }
 
430
        group, ok := n.groups[groupId]
 
431
        if !ok {
 
432
                return nil, testservices.NewSecurityGroupByIDNotFoundError(groupId)
 
433
        }
 
434
        return &group, nil
 
435
}
 
436
 
 
437
// securityGroupByName retrieves an existing named group.
 
438
func (n *Nova) securityGroupByName(groupName string) (*nova.SecurityGroup, error) {
 
439
        if err := n.ProcessFunctionHook(n, groupName); err != nil {
 
440
                return nil, err
 
441
        }
 
442
        for _, group := range n.groups {
 
443
                if group.Name == groupName {
 
444
                        return &group, nil
 
445
                }
 
446
        }
 
447
        return nil, testservices.NewSecurityGroupByNameNotFoundError(groupName)
 
448
}
 
449
 
 
450
// allSecurityGroups returns a list of all existing groups.
 
451
func (n *Nova) allSecurityGroups() []nova.SecurityGroup {
 
452
        var groups []nova.SecurityGroup
 
453
        for _, group := range n.groups {
 
454
                groups = append(groups, group)
 
455
        }
 
456
        return groups
 
457
}
 
458
 
 
459
// removeSecurityGroup deletes an existing group.
 
460
func (n *Nova) removeSecurityGroup(groupId string) error {
 
461
        if err := n.ProcessFunctionHook(n, groupId); err != nil {
 
462
                return err
 
463
        }
 
464
        if _, err := n.securityGroup(groupId); err != nil {
 
465
                return err
 
466
        }
 
467
        delete(n.groups, groupId)
 
468
        return nil
 
469
}
 
470
 
 
471
// addSecurityGroupRule creates a new rule in an existing group.
 
472
// This can be either an ingress or a group rule (see the notes
 
473
// about nova.RuleInfo).
 
474
func (n *Nova) addSecurityGroupRule(ruleId string, rule nova.RuleInfo) error {
 
475
        if err := n.ProcessFunctionHook(n, ruleId, rule); err != nil {
 
476
                return err
 
477
        }
 
478
        if _, err := n.securityGroupRule(ruleId); err == nil {
 
479
                return testservices.NewSecurityGroupRuleAlreadyExistsError(ruleId)
 
480
        }
 
481
        group, err := n.securityGroup(rule.ParentGroupId)
 
482
        if err != nil {
 
483
                return err
 
484
        }
 
485
        for _, ru := range group.Rules {
 
486
                if ru.Id == ruleId {
 
487
                        return testservices.NewCannotAddTwiceRuleToGroupError(ru.Id, group.Id)
 
488
                }
 
489
        }
 
490
        var zeroSecurityGroupRef nova.SecurityGroupRef
 
491
        newrule := nova.SecurityGroupRule{
 
492
                ParentGroupId: rule.ParentGroupId,
 
493
                Id:            ruleId,
 
494
                Group:         zeroSecurityGroupRef,
 
495
        }
 
496
        if rule.GroupId != nil {
 
497
                sourceGroup, err := n.securityGroup(*rule.GroupId)
 
498
                if err != nil {
 
499
                        return testservices.NewUnknownSecurityGroupError(*rule.GroupId)
 
500
                }
 
501
                newrule.Group = nova.SecurityGroupRef{
 
502
                        TenantId: sourceGroup.TenantId,
 
503
                        Name:     sourceGroup.Name,
 
504
                }
 
505
        } else if rule.Cidr == "" {
 
506
                // http://pad.lv/1226996
 
507
                // It seems that if you don't supply Cidr or GroupId then
 
508
                // Openstack treats the Cidr as 0.0.0.0/0
 
509
                // However, since that is not clearly specified we just panic()
 
510
                // because we don't want to rely on that behavior
 
511
                panic(fmt.Sprintf("Neither Cidr nor GroupId are set for this SecurityGroup Rule: %v", rule))
 
512
        }
 
513
        if rule.FromPort != 0 {
 
514
                newrule.FromPort = &rule.FromPort
 
515
        }
 
516
        if rule.ToPort != 0 {
 
517
                newrule.ToPort = &rule.ToPort
 
518
        }
 
519
        if rule.IPProtocol != "" {
 
520
                newrule.IPProtocol = &rule.IPProtocol
 
521
        }
 
522
        if rule.Cidr != "" {
 
523
                newrule.IPRange = make(map[string]string)
 
524
                newrule.IPRange["cidr"] = rule.Cidr
 
525
        }
 
526
 
 
527
        group.Rules = append(group.Rules, newrule)
 
528
        n.groups[group.Id] = *group
 
529
        n.rules[newrule.Id] = newrule
 
530
        return nil
 
531
}
 
532
 
 
533
// hasSecurityGroupRule returns whether the given group contains the given rule,
 
534
// or (when groupId="-1") whether the given rule exists.
 
535
func (n *Nova) hasSecurityGroupRule(groupId, ruleId string) bool {
 
536
        rule, ok := n.rules[ruleId]
 
537
        _, err := n.securityGroup(groupId)
 
538
        return ok && (groupId == "-1" || (err == nil && rule.ParentGroupId == groupId))
 
539
}
 
540
 
 
541
// securityGroupRule retrieves an existing rule by ID.
 
542
func (n *Nova) securityGroupRule(ruleId string) (*nova.SecurityGroupRule, error) {
 
543
        if err := n.ProcessFunctionHook(n, ruleId); err != nil {
 
544
                return nil, err
 
545
        }
 
546
        rule, ok := n.rules[ruleId]
 
547
        if !ok {
 
548
                return nil, testservices.NewSecurityGroupRuleNotFoundError(ruleId)
 
549
        }
 
550
        return &rule, nil
 
551
}
 
552
 
 
553
// removeSecurityGroupRule deletes an existing rule from its group.
 
554
func (n *Nova) removeSecurityGroupRule(ruleId string) error {
 
555
        if err := n.ProcessFunctionHook(n, ruleId); err != nil {
 
556
                return err
 
557
        }
 
558
        rule, err := n.securityGroupRule(ruleId)
 
559
        if err != nil {
 
560
                return err
 
561
        }
 
562
        if group, err := n.securityGroup(rule.ParentGroupId); err == nil {
 
563
                idx := -1
 
564
                for ri, ru := range group.Rules {
 
565
                        if ru.Id == ruleId {
 
566
                                idx = ri
 
567
                                break
 
568
                        }
 
569
                }
 
570
                if idx != -1 {
 
571
                        group.Rules = append(group.Rules[:idx], group.Rules[idx+1:]...)
 
572
                        n.groups[group.Id] = *group
 
573
                }
 
574
                // Silently ignore missing rules...
 
575
        }
 
576
        // ...or groups
 
577
        delete(n.rules, ruleId)
 
578
        return nil
 
579
}
 
580
 
 
581
// addServerSecurityGroup attaches an existing server to a group.
 
582
func (n *Nova) addServerSecurityGroup(serverId string, groupId string) error {
 
583
        if err := n.ProcessFunctionHook(n, serverId, groupId); err != nil {
 
584
                return err
 
585
        }
 
586
        if _, err := n.server(serverId); err != nil {
 
587
                return err
 
588
        }
 
589
        if _, err := n.securityGroup(groupId); err != nil {
 
590
                return err
 
591
        }
 
592
        groups, ok := n.serverGroups[serverId]
 
593
        if ok {
 
594
                for _, gid := range groups {
 
595
                        if gid == groupId {
 
596
                                return testservices.NewServerBelongsToGroupError(serverId, groupId)
 
597
                        }
 
598
                }
 
599
        }
 
600
        groups = append(groups, groupId)
 
601
        n.serverGroups[serverId] = groups
 
602
        return nil
 
603
}
 
604
 
 
605
// hasServerSecurityGroup returns whether the given server belongs to the group.
 
606
func (n *Nova) hasServerSecurityGroup(serverId string, groupId string) bool {
 
607
        if _, err := n.server(serverId); err != nil {
 
608
                return false
 
609
        }
 
610
        if _, err := n.securityGroup(groupId); err != nil {
 
611
                return false
 
612
        }
 
613
        groups, ok := n.serverGroups[serverId]
 
614
        if !ok {
 
615
                return false
 
616
        }
 
617
        for _, gid := range groups {
 
618
                if gid == groupId {
 
619
                        return true
 
620
                }
 
621
        }
 
622
        return false
 
623
}
 
624
 
 
625
// allServerSecurityGroups returns all security groups attached to the
 
626
// given server.
 
627
func (n *Nova) allServerSecurityGroups(serverId string) []nova.SecurityGroup {
 
628
        var groups []nova.SecurityGroup
 
629
        for _, gid := range n.serverGroups[serverId] {
 
630
                group, err := n.securityGroup(gid)
 
631
                if err != nil {
 
632
                        return nil
 
633
                }
 
634
                groups = append(groups, *group)
 
635
        }
 
636
        return groups
 
637
}
 
638
 
 
639
// removeServerSecurityGroup detaches an existing server from a group.
 
640
func (n *Nova) removeServerSecurityGroup(serverId string, groupId string) error {
 
641
        if err := n.ProcessFunctionHook(n, serverId, groupId); err != nil {
 
642
                return err
 
643
        }
 
644
        if _, err := n.server(serverId); err != nil {
 
645
                return err
 
646
        }
 
647
        if _, err := n.securityGroup(groupId); err != nil {
 
648
                return err
 
649
        }
 
650
        groups, ok := n.serverGroups[serverId]
 
651
        if !ok {
 
652
                return testservices.NewServerDoesNotBelongToGroupsError(serverId)
 
653
        }
 
654
        idx := -1
 
655
        for gi, gid := range groups {
 
656
                if gid == groupId {
 
657
                        idx = gi
 
658
                        break
 
659
                }
 
660
        }
 
661
        if idx == -1 {
 
662
                return testservices.NewServerDoesNotBelongToGroupError(serverId, groupId)
 
663
        }
 
664
        groups = append(groups[:idx], groups[idx+1:]...)
 
665
        n.serverGroups[serverId] = groups
 
666
        return nil
 
667
}
 
668
 
 
669
// addFloatingIP creates a new floating IP address in the pool.
 
670
func (n *Nova) addFloatingIP(ip nova.FloatingIP) error {
 
671
        if err := n.ProcessFunctionHook(n, ip); err != nil {
 
672
                return err
 
673
        }
 
674
        if _, err := n.floatingIP(ip.Id); err == nil {
 
675
                return testservices.NewFloatingIPExistsError(ip.Id)
 
676
        }
 
677
        n.floatingIPs[ip.Id] = ip
 
678
        return nil
 
679
}
 
680
 
 
681
// hasFloatingIP returns whether the given floating IP address exists.
 
682
func (n *Nova) hasFloatingIP(address string) bool {
 
683
        if len(n.floatingIPs) == 0 {
 
684
                return false
 
685
        }
 
686
        for _, fip := range n.floatingIPs {
 
687
                if fip.IP == address {
 
688
                        return true
 
689
                }
 
690
        }
 
691
        return false
 
692
}
 
693
 
 
694
// floatingIP retrieves the floating IP by ID.
 
695
func (n *Nova) floatingIP(ipId string) (*nova.FloatingIP, error) {
 
696
        if err := n.ProcessFunctionHook(n, ipId); err != nil {
 
697
                return nil, err
 
698
        }
 
699
        ip, ok := n.floatingIPs[ipId]
 
700
        if !ok {
 
701
                return nil, testservices.NewFloatingIPNotFoundError(ipId)
 
702
        }
 
703
        return &ip, nil
 
704
}
 
705
 
 
706
// floatingIPByAddr retrieves the floating IP by address.
 
707
func (n *Nova) floatingIPByAddr(address string) (*nova.FloatingIP, error) {
 
708
        if err := n.ProcessFunctionHook(n, address); err != nil {
 
709
                return nil, err
 
710
        }
 
711
        for _, fip := range n.floatingIPs {
 
712
                if fip.IP == address {
 
713
                        return &fip, nil
 
714
                }
 
715
        }
 
716
        return nil, testservices.NewFloatingIPNotFoundError(address)
 
717
}
 
718
 
 
719
// allFloatingIPs returns a list of all created floating IPs.
 
720
func (n *Nova) allFloatingIPs() []nova.FloatingIP {
 
721
        var fips []nova.FloatingIP
 
722
        for _, fip := range n.floatingIPs {
 
723
                fips = append(fips, fip)
 
724
        }
 
725
        return fips
 
726
}
 
727
 
 
728
// removeFloatingIP deletes an existing floating IP by ID.
 
729
func (n *Nova) removeFloatingIP(ipId string) error {
 
730
        if err := n.ProcessFunctionHook(n, ipId); err != nil {
 
731
                return err
 
732
        }
 
733
        if _, err := n.floatingIP(ipId); err != nil {
 
734
                return err
 
735
        }
 
736
        delete(n.floatingIPs, ipId)
 
737
        return nil
 
738
}
 
739
 
 
740
// addServerFloatingIP attaches an existing floating IP to a server.
 
741
func (n *Nova) addServerFloatingIP(serverId string, ipId string) error {
 
742
        if err := n.ProcessFunctionHook(n, serverId, ipId); err != nil {
 
743
                return err
 
744
        }
 
745
        if _, err := n.server(serverId); err != nil {
 
746
                return err
 
747
        }
 
748
        if fip, err := n.floatingIP(ipId); err != nil {
 
749
                return err
 
750
        } else {
 
751
                fixedIP := "4.3.2.1" // not important really, unused
 
752
                fip.FixedIP = &fixedIP
 
753
                fip.InstanceId = &serverId
 
754
                n.floatingIPs[ipId] = *fip
 
755
        }
 
756
        fips, ok := n.serverIPs[serverId]
 
757
        if ok {
 
758
                for _, fipId := range fips {
 
759
                        if fipId == ipId {
 
760
                                return testservices.NewServerHasFloatingIPError(serverId, ipId)
 
761
                        }
 
762
                }
 
763
        }
 
764
        fips = append(fips, ipId)
 
765
        n.serverIPs[serverId] = fips
 
766
        return nil
 
767
}
 
768
 
 
769
// hasServerFloatingIP verifies the given floating IP belongs to a server.
 
770
func (n *Nova) hasServerFloatingIP(serverId, address string) bool {
 
771
        if _, err := n.server(serverId); err != nil || !n.hasFloatingIP(address) {
 
772
                return false
 
773
        }
 
774
        fips, ok := n.serverIPs[serverId]
 
775
        if !ok {
 
776
                return false
 
777
        }
 
778
        for _, fipId := range fips {
 
779
                fip := n.floatingIPs[fipId]
 
780
                if fip.IP == address {
 
781
                        return true
 
782
                }
 
783
        }
 
784
        return false
 
785
}
 
786
 
 
787
// removeServerFloatingIP deletes an attached floating IP from a server.
 
788
func (n *Nova) removeServerFloatingIP(serverId string, ipId string) error {
 
789
        if err := n.ProcessFunctionHook(n, serverId); err != nil {
 
790
                return err
 
791
        }
 
792
        if _, err := n.server(serverId); err != nil {
 
793
                return err
 
794
        }
 
795
        if fip, err := n.floatingIP(ipId); err != nil {
 
796
                return err
 
797
        } else {
 
798
                fip.FixedIP = nil
 
799
                fip.InstanceId = nil
 
800
                n.floatingIPs[ipId] = *fip
 
801
        }
 
802
        fips, ok := n.serverIPs[serverId]
 
803
        if !ok {
 
804
                return testservices.NewNoFloatingIPsToRemoveError(serverId)
 
805
        }
 
806
        idx := -1
 
807
        for fi, fipId := range fips {
 
808
                if fipId == ipId {
 
809
                        idx = fi
 
810
                        break
 
811
                }
 
812
        }
 
813
        if idx == -1 {
 
814
                return testservices.NewNoFloatingIPsError(serverId, ipId)
 
815
        }
 
816
        fips = append(fips[:idx], fips[idx+1:]...)
 
817
        n.serverIPs[serverId] = fips
 
818
        return nil
 
819
}
 
820
 
 
821
// allNetworks returns a list of all existing networks.
 
822
func (n *Nova) allNetworks() (networks []nova.Network) {
 
823
        for _, net := range n.networks {
 
824
                networks = append(networks, net)
 
825
        }
 
826
        return networks
 
827
}
 
828
 
 
829
// allAvailabilityZones returns a list of all existing availability zones,
 
830
// sorted by name.
 
831
func (n *Nova) allAvailabilityZones() (zones []nova.AvailabilityZone) {
 
832
        for _, zone := range n.availabilityZones {
 
833
                zones = append(zones, zone)
 
834
        }
 
835
        sort.Sort(azByName(zones))
 
836
        return zones
 
837
}
 
838
 
 
839
type azByName []nova.AvailabilityZone
 
840
 
 
841
func (a azByName) Len() int {
 
842
        return len(a)
 
843
}
 
844
 
 
845
func (a azByName) Less(i, j int) bool {
 
846
        return a[i].Name < a[j].Name
 
847
}
 
848
 
 
849
func (a azByName) Swap(i, j int) {
 
850
        a[i], a[j] = a[j], a[i]
 
851
}
 
852
 
 
853
// setServerMetadata sets metadata on a server.
 
854
func (n *Nova) setServerMetadata(serverId string, metadata map[string]string) error {
 
855
        if err := n.ProcessFunctionHook(n, serverId, metadata); err != nil {
 
856
                return err
 
857
        }
 
858
        server, err := n.server(serverId)
 
859
        if err != nil {
 
860
                return err
 
861
        }
 
862
        if server.Metadata == nil {
 
863
                server.Metadata = make(map[string]string)
 
864
        }
 
865
        for k, v := range metadata {
 
866
                server.Metadata[k] = v
 
867
        }
 
868
        n.servers[serverId] = *server
 
869
        return nil
 
870
}