OLD | NEW |
(Empty) | |
| 1 // Nova double testing service - internal direct API implementation |
| 2 |
| 3 package novaservice |
| 4 |
| 5 import ( |
| 6 "fmt" |
| 7 "launchpad.net/goose/nova" |
| 8 "strings" |
| 9 ) |
| 10 |
| 11 // Nova contains the service double's internal state. |
| 12 type Nova struct { |
| 13 flavors map[string]nova.FlavorDetail |
| 14 servers map[string]nova.ServerDetail |
| 15 groups map[int]nova.SecurityGroup |
| 16 rules map[int]nova.SecurityGroupRule |
| 17 floatingIPs map[int]nova.FloatingIP |
| 18 serverGroups map[string][]int |
| 19 serverIPs map[string][]int |
| 20 hostname string |
| 21 baseURL string |
| 22 token string |
| 23 } |
| 24 |
| 25 // New creates an instance of the Nova object, given the parameters. |
| 26 func New(hostname, baseURL, token string) *Nova { |
| 27 nova := &Nova{ |
| 28 flavors: make(map[string]nova.FlavorDetail), |
| 29 servers: make(map[string]nova.ServerDetail), |
| 30 groups: make(map[int]nova.SecurityGroup), |
| 31 rules: make(map[int]nova.SecurityGroupRule), |
| 32 floatingIPs: make(map[int]nova.FloatingIP), |
| 33 serverGroups: make(map[string][]int), |
| 34 serverIPs: make(map[string][]int), |
| 35 hostname: hostname, |
| 36 baseURL: baseURL, |
| 37 token: token, |
| 38 } |
| 39 return nova |
| 40 } |
| 41 |
| 42 // buildFlavorLinks populates the Links field of the passed |
| 43 // FlavorDetail as needed by OpenStack HTTP API. Call this |
| 44 // before addFlavor(). |
| 45 func (n *Nova) buildFlavorLinks(flavor *nova.FlavorDetail) { |
| 46 ep := n.hostname |
| 47 ver := strings.TrimLeft(n.baseURL, "/") |
| 48 url := n.token + "/flavors/" + flavor.Id |
| 49 flavor.Links = []nova.Link{ |
| 50 nova.Link{Href: ep + ver + url, Rel: "self"}, |
| 51 nova.Link{Href: ep + url, Rel: "bookmark"}, |
| 52 } |
| 53 } |
| 54 |
| 55 // addFlavor creates a new flavor. |
| 56 func (n *Nova) addFlavor(flavor nova.FlavorDetail) error { |
| 57 if _, err := n.getFlavor(flavor.Id); err == nil { |
| 58 return fmt.Errorf("a flavor with id %q already exists", flavor.I
d) |
| 59 } |
| 60 n.flavors[flavor.Id] = flavor |
| 61 return nil |
| 62 } |
| 63 |
| 64 // getFlavor retrieves an existing flavor by ID. |
| 65 func (n *Nova) getFlavor(flavorId string) (*nova.FlavorDetail, error) { |
| 66 flavor, ok := n.flavors[flavorId] |
| 67 if !ok { |
| 68 return nil, fmt.Errorf("no such flavor %q", flavorId) |
| 69 } |
| 70 return &flavor, nil |
| 71 } |
| 72 |
| 73 // getFlavorAsEntity returns the stored FlavorDetail as Entity. |
| 74 func (n *Nova) getFlavorAsEntity(flavorId string) (*nova.Entity, error) { |
| 75 flavor, err := n.getFlavor(flavorId) |
| 76 if err != nil { |
| 77 return nil, err |
| 78 } |
| 79 return &nova.Entity{ |
| 80 Id: flavor.Id, |
| 81 Name: flavor.Name, |
| 82 Links: flavor.Links, |
| 83 }, nil |
| 84 } |
| 85 |
| 86 // allFlavors returns a list of all existing flavors. |
| 87 func (n *Nova) allFlavors() []nova.FlavorDetail { |
| 88 var flavors []nova.FlavorDetail |
| 89 for _, flavor := range n.flavors { |
| 90 flavors = append(flavors, flavor) |
| 91 } |
| 92 return flavors |
| 93 } |
| 94 |
| 95 // allFlavorsAsEntities returns all flavors as Entity structs. |
| 96 func (n *Nova) allFlavorsAsEntities() []nova.Entity { |
| 97 var entities []nova.Entity |
| 98 for _, flavor := range n.flavors { |
| 99 entities = append(entities, nova.Entity{ |
| 100 Id: flavor.Id, |
| 101 Name: flavor.Name, |
| 102 Links: flavor.Links, |
| 103 }) |
| 104 } |
| 105 return entities |
| 106 } |
| 107 |
| 108 // removeFlavor deletes an existing flavor. |
| 109 func (n *Nova) removeFlavor(flavorId string) error { |
| 110 if _, err := n.getFlavor(flavorId); err != nil { |
| 111 return err |
| 112 } |
| 113 delete(n.flavors, flavorId) |
| 114 return nil |
| 115 } |
| 116 |
| 117 // buildServerLinks populates the Links field of the passed |
| 118 // ServerDetail as needed by OpenStack HTTP API. Call this |
| 119 // before addServer(). |
| 120 func (n *Nova) buildServerLinks(server *nova.ServerDetail) { |
| 121 ep := n.hostname |
| 122 ver := strings.TrimLeft(n.baseURL, "/") |
| 123 url := n.token + "/servers/" + server.Id |
| 124 server.Links = []nova.Link{ |
| 125 nova.Link{Href: ep + ver + url, Rel: "self"}, |
| 126 nova.Link{Href: ep + url, Rel: "bookmark"}, |
| 127 } |
| 128 } |
| 129 |
| 130 // addServer creates a new server. |
| 131 func (n *Nova) addServer(server nova.ServerDetail) error { |
| 132 if _, err := n.getServer(server.Id); err == nil { |
| 133 return fmt.Errorf("a server with id %q already exists", server.I
d) |
| 134 } |
| 135 n.servers[server.Id] = server |
| 136 return nil |
| 137 } |
| 138 |
| 139 // getServer retrieves an existing server by ID. |
| 140 func (n *Nova) getServer(serverId string) (*nova.ServerDetail, error) { |
| 141 server, ok := n.servers[serverId] |
| 142 if !ok { |
| 143 return nil, fmt.Errorf("no such server %q", serverId) |
| 144 } |
| 145 return &server, nil |
| 146 } |
| 147 |
| 148 // getServerAsEntity returns the stored ServerDetail as Entity. |
| 149 func (n *Nova) getServerAsEntity(serverId string) (*nova.Entity, error) { |
| 150 server, err := n.getServer(serverId) |
| 151 if err != nil { |
| 152 return nil, err |
| 153 } |
| 154 return &nova.Entity{ |
| 155 Id: server.Id, |
| 156 Name: server.Name, |
| 157 Links: server.Links, |
| 158 }, nil |
| 159 } |
| 160 |
| 161 // allServers returns a list of all existing servers. |
| 162 func (n *Nova) allServers() []nova.ServerDetail { |
| 163 var servers []nova.ServerDetail |
| 164 for _, server := range n.servers { |
| 165 servers = append(servers, server) |
| 166 } |
| 167 return servers |
| 168 } |
| 169 |
| 170 // allServersAsEntities returns all servers as Entity structs. |
| 171 func (n *Nova) allServersAsEntities() []nova.Entity { |
| 172 var entities []nova.Entity |
| 173 for _, server := range n.servers { |
| 174 entities = append(entities, nova.Entity{ |
| 175 Id: server.Id, |
| 176 Name: server.Name, |
| 177 Links: server.Links, |
| 178 }) |
| 179 } |
| 180 return entities |
| 181 } |
| 182 |
| 183 // removeServer deletes an existing server. |
| 184 func (n *Nova) removeServer(serverId string) error { |
| 185 if _, err := n.getServer(serverId); err != nil { |
| 186 return err |
| 187 } |
| 188 delete(n.servers, serverId) |
| 189 return nil |
| 190 } |
| 191 |
| 192 // addSecurityGroup creates a new security group. |
| 193 func (n *Nova) addSecurityGroup(group nova.SecurityGroup) error { |
| 194 if _, err := n.getSecurityGroup(group.Id); err == nil { |
| 195 return fmt.Errorf("a security group with id %d already exists",
group.Id) |
| 196 } |
| 197 n.groups[group.Id] = group |
| 198 return nil |
| 199 } |
| 200 |
| 201 // getSecurityGroup retrieves an existing group by ID. |
| 202 func (n *Nova) getSecurityGroup(groupId int) (*nova.SecurityGroup, error) { |
| 203 group, ok := n.groups[groupId] |
| 204 if !ok { |
| 205 return nil, fmt.Errorf("no such security group %d", groupId) |
| 206 } |
| 207 return &group, nil |
| 208 } |
| 209 |
| 210 // allSecurityGroups returns a list of all existing groups. |
| 211 func (n *Nova) allSecurityGroups() []nova.SecurityGroup { |
| 212 var groups []nova.SecurityGroup |
| 213 for _, group := range n.groups { |
| 214 groups = append(groups, group) |
| 215 } |
| 216 return groups |
| 217 } |
| 218 |
| 219 // removeSecurityGroup deletes an existing group. |
| 220 func (n *Nova) removeSecurityGroup(groupId int) error { |
| 221 if _, err := n.getSecurityGroup(groupId); err != nil { |
| 222 return err |
| 223 } |
| 224 delete(n.groups, groupId) |
| 225 return nil |
| 226 } |
| 227 |
| 228 // addSecurityGroupRule creates a new rule in an existing group. |
| 229 // This can be either an ingress or a group rule (see the notes |
| 230 // about nova.RuleInfo). |
| 231 func (n *Nova) addSecurityGroupRule(ruleId int, rule nova.RuleInfo) error { |
| 232 if _, err := n.getSecurityGroupRule(ruleId); err == nil { |
| 233 return fmt.Errorf("a security group rule with id %d already exis
ts", ruleId) |
| 234 } |
| 235 group, err := n.getSecurityGroup(rule.ParentGroupId) |
| 236 if err != nil { |
| 237 return err |
| 238 } |
| 239 for _, ru := range group.Rules { |
| 240 if ru.Id == ruleId { |
| 241 return fmt.Errorf("cannot add twice rule %d to security
group %d", ru.Id, group.Id) |
| 242 } |
| 243 } |
| 244 newrule := nova.SecurityGroupRule{ |
| 245 ParentGroupId: rule.ParentGroupId, |
| 246 Id: ruleId, |
| 247 } |
| 248 if rule.GroupId != nil { |
| 249 sourceGroup, err := n.getSecurityGroup(*rule.GroupId) |
| 250 if err != nil { |
| 251 return fmt.Errorf("unknown source security group %d", *r
ule.GroupId) |
| 252 } |
| 253 newrule.Group = &nova.SecurityGroupRef{ |
| 254 TenantId: sourceGroup.TenantId, |
| 255 Name: sourceGroup.Name, |
| 256 } |
| 257 } |
| 258 if rule.FromPort > 0 { |
| 259 newrule.FromPort = &rule.FromPort |
| 260 } |
| 261 if rule.ToPort > 0 { |
| 262 newrule.ToPort = &rule.ToPort |
| 263 } |
| 264 if rule.IPProtocol != "" { |
| 265 newrule.IPProtocol = &rule.IPProtocol |
| 266 } |
| 267 if rule.Cidr != "" { |
| 268 newrule.IPRange = make(map[string]string) |
| 269 newrule.IPRange["cidr"] = rule.Cidr |
| 270 } |
| 271 |
| 272 group.Rules = append(group.Rules, newrule) |
| 273 n.groups[group.Id] = *group |
| 274 n.rules[newrule.Id] = newrule |
| 275 return nil |
| 276 } |
| 277 |
| 278 // hasSecurityGroupRule returns whether the given group contains the given rule, |
| 279 // or (when groupId=-1) whether the given rule exists. |
| 280 func (n *Nova) hasSecurityGroupRule(groupId, ruleId int) bool { |
| 281 rule, ok := n.rules[ruleId] |
| 282 _, err := n.getSecurityGroup(groupId) |
| 283 return ok && (groupId == -1 || (err == nil && rule.ParentGroupId == grou
pId)) |
| 284 } |
| 285 |
| 286 // getSecurityGroupRule retrieves an existing rule by ID. |
| 287 func (n *Nova) getSecurityGroupRule(ruleId int) (*nova.SecurityGroupRule, error)
{ |
| 288 rule, ok := n.rules[ruleId] |
| 289 if !ok { |
| 290 return nil, fmt.Errorf("no such security group rule %d", ruleId) |
| 291 } |
| 292 return &rule, nil |
| 293 } |
| 294 |
| 295 // removeSecurityGroupRule deletes an existing rule from its group. |
| 296 func (n *Nova) removeSecurityGroupRule(ruleId int) error { |
| 297 rule, err := n.getSecurityGroupRule(ruleId) |
| 298 if err != nil { |
| 299 return err |
| 300 } |
| 301 if group, err := n.getSecurityGroup(rule.ParentGroupId); err == nil { |
| 302 idx := -1 |
| 303 for ri, ru := range group.Rules { |
| 304 if ru.Id == ruleId { |
| 305 idx = ri |
| 306 break |
| 307 } |
| 308 } |
| 309 if idx != -1 { |
| 310 group.Rules = append(group.Rules[:idx], group.Rules[idx+
1:]...) |
| 311 n.groups[group.Id] = *group |
| 312 } |
| 313 // Silently ignore missing rules... |
| 314 } |
| 315 // ...or groups |
| 316 delete(n.rules, ruleId) |
| 317 return nil |
| 318 } |
| 319 |
| 320 // addServerSecurityGroup attaches an existing server to a group. |
| 321 func (n *Nova) addServerSecurityGroup(serverId string, groupId int) error { |
| 322 if _, err := n.getServer(serverId); err != nil { |
| 323 return err |
| 324 } |
| 325 if _, err := n.getSecurityGroup(groupId); err != nil { |
| 326 return err |
| 327 } |
| 328 groups, ok := n.serverGroups[serverId] |
| 329 if ok { |
| 330 for _, gid := range groups { |
| 331 if gid == groupId { |
| 332 return fmt.Errorf("server %q already belongs to
group %d", serverId, groupId) |
| 333 } |
| 334 } |
| 335 } |
| 336 groups = append(groups, groupId) |
| 337 n.serverGroups[serverId] = groups |
| 338 return nil |
| 339 } |
| 340 |
| 341 // hasServerSecurityGroup returns whether the given server belongs to the group. |
| 342 func (n *Nova) hasServerSecurityGroup(serverId string, groupId int) bool { |
| 343 if _, err := n.getServer(serverId); err != nil { |
| 344 return false |
| 345 } |
| 346 if _, err := n.getSecurityGroup(groupId); err != nil { |
| 347 return false |
| 348 } |
| 349 groups, ok := n.serverGroups[serverId] |
| 350 if !ok { |
| 351 return false |
| 352 } |
| 353 for _, gid := range groups { |
| 354 if gid == groupId { |
| 355 return true |
| 356 } |
| 357 } |
| 358 return false |
| 359 } |
| 360 |
| 361 // removeServerSecurityGroup detaches an existing server from a group. |
| 362 func (n *Nova) removeServerSecurityGroup(serverId string, groupId int) error { |
| 363 if _, err := n.getServer(serverId); err != nil { |
| 364 return err |
| 365 } |
| 366 if _, err := n.getSecurityGroup(groupId); err != nil { |
| 367 return err |
| 368 } |
| 369 groups, ok := n.serverGroups[serverId] |
| 370 if !ok { |
| 371 return fmt.Errorf("server %q does not belong to any groups", ser
verId) |
| 372 } |
| 373 idx := -1 |
| 374 for gi, gid := range groups { |
| 375 if gid == groupId { |
| 376 idx = gi |
| 377 break |
| 378 } |
| 379 } |
| 380 if idx == -1 { |
| 381 return fmt.Errorf("server %q does not belong to group %d", serve
rId, groupId) |
| 382 } |
| 383 groups = append(groups[:idx], groups[idx+1:]...) |
| 384 n.serverGroups[serverId] = groups |
| 385 return nil |
| 386 } |
| 387 |
| 388 // addFloatingIP creates a new floating IP address in the pool. |
| 389 func (n *Nova) addFloatingIP(ip nova.FloatingIP) error { |
| 390 if _, err := n.getFloatingIP(ip.Id); err == nil { |
| 391 return fmt.Errorf("a floating IP with id %d already exists", ip.
Id) |
| 392 } |
| 393 n.floatingIPs[ip.Id] = ip |
| 394 return nil |
| 395 } |
| 396 |
| 397 // hasFloatingIP returns whether the given floating IP address exists. |
| 398 func (n *Nova) hasFloatingIP(address string) bool { |
| 399 if len(n.floatingIPs) == 0 { |
| 400 return false |
| 401 } |
| 402 for _, fip := range n.floatingIPs { |
| 403 if fip.IP == address { |
| 404 return true |
| 405 } |
| 406 } |
| 407 return false |
| 408 } |
| 409 |
| 410 // getFloatingIP retrieves the floating IP by ID. |
| 411 func (n *Nova) getFloatingIP(ipId int) (*nova.FloatingIP, error) { |
| 412 ip, ok := n.floatingIPs[ipId] |
| 413 if !ok { |
| 414 return nil, fmt.Errorf("no such floating IP %d", ipId) |
| 415 } |
| 416 return &ip, nil |
| 417 } |
| 418 |
| 419 // allFloatingIPs returns a list of all created floating IPs. |
| 420 func (n *Nova) allFloatingIPs() []nova.FloatingIP { |
| 421 var fips []nova.FloatingIP |
| 422 for _, fip := range n.floatingIPs { |
| 423 fips = append(fips, fip) |
| 424 } |
| 425 return fips |
| 426 } |
| 427 |
| 428 // removeFloatingIP deletes an existing floating IP by ID. |
| 429 func (n *Nova) removeFloatingIP(ipId int) error { |
| 430 if _, err := n.getFloatingIP(ipId); err != nil { |
| 431 return err |
| 432 } |
| 433 delete(n.floatingIPs, ipId) |
| 434 return nil |
| 435 } |
| 436 |
| 437 // addServerFloatingIP attaches an existing floating IP to a server. |
| 438 func (n *Nova) addServerFloatingIP(serverId string, ipId int) error { |
| 439 if _, err := n.getServer(serverId); err != nil { |
| 440 return err |
| 441 } |
| 442 if _, err := n.getFloatingIP(ipId); err != nil { |
| 443 return err |
| 444 } |
| 445 fips, ok := n.serverIPs[serverId] |
| 446 if ok { |
| 447 for _, fipId := range fips { |
| 448 if fipId == ipId { |
| 449 return fmt.Errorf("server %q already has floatin
g IP %d", serverId, ipId) |
| 450 } |
| 451 } |
| 452 } |
| 453 fips = append(fips, ipId) |
| 454 n.serverIPs[serverId] = fips |
| 455 return nil |
| 456 } |
| 457 |
| 458 // hasServerFloatingIP verifies the given floating IP belongs to a server. |
| 459 func (n *Nova) hasServerFloatingIP(serverId, address string) bool { |
| 460 if _, err := n.getServer(serverId); err != nil || !n.hasFloatingIP(addre
ss) { |
| 461 return false |
| 462 } |
| 463 fips, ok := n.serverIPs[serverId] |
| 464 if !ok { |
| 465 return false |
| 466 } |
| 467 for _, fipId := range fips { |
| 468 fip := n.floatingIPs[fipId] |
| 469 if fip.IP == address { |
| 470 return true |
| 471 } |
| 472 } |
| 473 return false |
| 474 } |
| 475 |
| 476 // removeServerFloatingIP deletes an attached floating IP from a server. |
| 477 func (n *Nova) removeServerFloatingIP(serverId string, ipId int) error { |
| 478 if _, err := n.getServer(serverId); err != nil { |
| 479 return err |
| 480 } |
| 481 if _, err := n.getFloatingIP(ipId); err != nil { |
| 482 return err |
| 483 } |
| 484 fips, ok := n.serverIPs[serverId] |
| 485 if !ok { |
| 486 return fmt.Errorf("server %q does not have any floating IPs to r
emove", serverId) |
| 487 } |
| 488 idx := -1 |
| 489 for fi, fipId := range fips { |
| 490 if fipId == ipId { |
| 491 idx = fi |
| 492 break |
| 493 } |
| 494 } |
| 495 if idx == -1 { |
| 496 return fmt.Errorf("server %q does not have floating IP %d", serv
erId, ipId) |
| 497 } |
| 498 fips = append(fips[:idx], fips[idx+1:]...) |
| 499 n.serverIPs[serverId] = fips |
| 500 return nil |
| 501 } |
OLD | NEW |