Left: | ||
Right: |
OLD | NEW |
---|---|
1 // The nova package provides a way to access the OpenStack Compute APIs. | 1 // The nova package provides a way to access the OpenStack Compute APIs. |
2 // See http://docs.openstack.org/api/openstack-compute/2/content/. | 2 // See http://docs.openstack.org/api/openstack-compute/2/content/. |
3 package nova | 3 package nova |
4 | 4 |
5 import ( | 5 import ( |
6 "encoding/base64" | 6 "encoding/base64" |
7 "fmt" | 7 "fmt" |
8 "launchpad.net/goose/client" | 8 "launchpad.net/goose/client" |
9 goosehttp "launchpad.net/goose/http" | 9 goosehttp "launchpad.net/goose/http" |
10 "net/http" | 10 "net/http" |
11 "net/url" | |
11 ) | 12 ) |
12 | 13 |
13 const ( | 14 const ( |
14 apiFlavors = "/flavors" | 15 apiFlavors = "/flavors" |
15 apiFlavorsDetail = "/flavors/detail" | 16 apiFlavorsDetail = "/flavors/detail" |
16 apiServers = "/servers" | 17 apiServers = "/servers" |
17 apiServersDetail = "/servers/detail" | 18 apiServersDetail = "/servers/detail" |
18 apiSecurityGroups = "/os-security-groups" | 19 apiSecurityGroups = "/os-security-groups" |
19 apiSecurityGroupRules = "/os-security-group-rules" | 20 apiSecurityGroupRules = "/os-security-group-rules" |
20 apiFloatingIPs = "/os-floating-ips" | 21 apiFloatingIPs = "/os-floating-ips" |
21 ) | 22 ) |
22 | 23 |
24 const ( | |
jameinel
2012/12/04 13:01:45
I wonder if these should be typed as well as group
wallyworld
2012/12/05 00:46:41
I considered it but then filter.Add(key, value) wo
| |
25 // Server status values. | |
26 StatusActive = "ACTIVE" // The server is active. | |
27 StatusBuild = "BUILD" // The server has not finished the original build process. | |
28 StatusDeleted = "DELETED" // The server is deleted. | |
29 StatusError = "ERROR" // The server is in error. | |
30 StatusHardReboot = "HARD_REBOOT" // The server is hard rebooting. | |
31 StatusPassword = "PASSWORD" // The password is being reset on t he server. | |
32 StatusReboot = "REBOOT" // The server is in a soft reboot s tate. | |
33 StatusRebuild = "REBUILD" // The server is currently being re built from an image. | |
34 StatusRescue = "RESCUE" // The server is in rescue mode. | |
35 StatusResize = "RESIZE" // Server is performing the differe ntial copy of data that changed during its initial copy. | |
36 StatusShutoff = "SHUTOFF" // The virtual machine (VM) was pow ered down by the user, but not through the OpenStack Compute API. | |
37 StatusSuspended = "SUSPENDED" // The server is suspended, either by request or necessity. | |
38 StatusUnknown = "UNKNOWN" // The state of the server is unkno wn. Contact your cloud provider. | |
39 StatusVerifyResize = "VERIFY_RESIZE" // System is awaiting confirmation that the server is operational after a move or resize. | |
40 ) | |
41 | |
42 const ( | |
43 // Filter keys. | |
44 FilterStatus = "status" // The server status. See Server St atus Values. | |
45 FilterImage = "image" // The image reference specified as an ID or full URL. | |
46 FilterFlavor = "flavor" // The flavor reference specified a s an ID or full URL. | |
47 FilterServer = "name" // The server name. | |
48 FilterMarker = "marker" // The ID of the last item in the p revious list. | |
49 FilterLimit = "limit" // The page size. | |
50 FilterChangesSince = "changes-since" // The changes-since time. The list contains servers that have been deleted since the changes-since time. | |
51 ) | |
52 | |
23 // Client provides a means to access the OpenStack Compute Service. | 53 // Client provides a means to access the OpenStack Compute Service. |
24 type Client struct { | 54 type Client struct { |
25 client client.Client | 55 client client.Client |
26 } | 56 } |
27 | 57 |
28 func New(client client.Client) *Client { | 58 func New(client client.Client) *Client { |
29 return &Client{client} | 59 return &Client{client} |
30 } | 60 } |
31 | 61 |
62 // ---------------------------------------------------------------------------- | |
63 // Filtering helper. | |
64 | |
65 // Filter builds filtering parameters to be used in an OpenStack query which sup ports | |
66 // filtering. For example: | |
67 // | |
68 // filter := NewFilter() | |
69 // filter.Add("name", "server_name") | |
70 // filter.Add("status", "ACTIVE") | |
71 // resp, err := nova.ListServers(filter) | |
jameinel
2012/12/04 13:01:45
Maybe it wouldn't help, as we don't have "filter.A
wallyworld
2012/12/05 00:46:41
Add is used for all sorts of filter keys: status,
| |
72 // | |
73 type Filter struct { | |
74 url.Values | |
75 } | |
76 | |
77 // NewFilter creates a new Filter. | |
78 func NewFilter() *Filter { | |
79 return &Filter{make(url.Values)} | |
80 } | |
81 | |
32 type Link struct { | 82 type Link struct { |
33 Href string | 83 Href string |
34 Rel string | 84 Rel string |
35 Type string | 85 Type string |
36 } | 86 } |
37 | 87 |
38 // Entity can describe a flavor, flavor detail or server. | 88 // Entity can describe a flavor, flavor detail or server. |
39 // Contains a list of links. | 89 // Contains a list of links. |
40 type Entity struct { | 90 type Entity struct { |
41 Id string | 91 Id string |
(...skipping 14 matching lines...) Expand all Loading... | |
56 type FlavorDetail struct { | 106 type FlavorDetail struct { |
57 Name string | 107 Name string |
58 RAM int | 108 RAM int |
59 VCPUs int | 109 VCPUs int |
60 Disk int | 110 Disk int |
61 Id string | 111 Id string |
62 Swap interface{} // Can be an empty string (?!) | 112 Swap interface{} // Can be an empty string (?!) |
63 } | 113 } |
64 | 114 |
65 // ListFlavorsDetail lists all details for available flavors. | 115 // ListFlavorsDetail lists all details for available flavors. |
66 func (c *Client) ListFlavorsDetail() (*[]FlavorDetail, error) { | 116 func (c *Client) ListFlavorsDetail() ([]FlavorDetail, error) { |
67 var resp struct { | 117 var resp struct { |
68 Flavors []FlavorDetail | 118 Flavors []FlavorDetail |
69 } | 119 } |
70 requestData := goosehttp.RequestData{RespValue: &resp} | 120 requestData := goosehttp.RequestData{RespValue: &resp} |
71 err := c.client.SendRequest(client.GET, "compute", apiFlavorsDetail, &re questData, | 121 err := c.client.SendRequest(client.GET, "compute", apiFlavorsDetail, &re questData, |
72 "failed to get list of flavors details") | 122 "failed to get list of flavors details") |
73 if err != nil { | 123 if err != nil { |
74 return nil, err | 124 return nil, err |
75 } | 125 } |
76 » return &resp.Flavors, nil | 126 » return resp.Flavors, nil |
77 } | 127 } |
78 | 128 |
79 // ListServers lists IDs, names, and links for all servers. | 129 // ListServers lists IDs, names, and links for all servers. |
80 func (c *Client) ListServers() (*[]Entity, error) { | 130 func (c *Client) ListServers(filter *Filter) ([]Entity, error) { |
81 var resp struct { | 131 var resp struct { |
82 Servers []Entity | 132 Servers []Entity |
83 } | 133 } |
84 » requestData := goosehttp.RequestData{RespValue: &resp, ExpectedStatus: [ ]int{http.StatusOK}} | 134 » requestData := goosehttp.RequestData{RespValue: &resp, Params: &filter.V alues, ExpectedStatus: []int{http.StatusOK}} |
85 err := c.client.SendRequest(client.GET, "compute", apiServers, &requestD ata, | 135 err := c.client.SendRequest(client.GET, "compute", apiServers, &requestD ata, |
86 "failed to get list of servers") | 136 "failed to get list of servers") |
87 if err != nil { | 137 if err != nil { |
88 return nil, err | 138 return nil, err |
89 } | 139 } |
90 » return &resp.Servers, nil | 140 » return resp.Servers, nil |
91 } | 141 } |
92 | 142 |
93 type ServerDetail struct { | 143 type ServerDetail struct { |
94 AddressIPv4 string | 144 AddressIPv4 string |
95 AddressIPv6 string | 145 AddressIPv6 string |
96 Created string | 146 Created string |
97 Flavor Entity | 147 Flavor Entity |
98 HostId string | 148 HostId string |
99 Id string | 149 Id string |
100 Image Entity | 150 Image Entity |
101 Links []Link | 151 Links []Link |
102 Name string | 152 Name string |
103 Progress int | 153 Progress int |
104 Status string | 154 Status string |
105 TenantId string `json:"tenant_id"` | 155 TenantId string `json:"tenant_id"` |
106 Updated string | 156 Updated string |
107 UserId string `json:"user_id"` | 157 UserId string `json:"user_id"` |
108 } | 158 } |
109 | 159 |
110 // ListServersDetail lists all details for available servers. | 160 // ListServersDetail lists all details for available servers. |
111 func (c *Client) ListServersDetail() (*[]ServerDetail, error) { | 161 func (c *Client) ListServersDetail(filter *Filter) ([]ServerDetail, error) { |
112 var resp struct { | 162 var resp struct { |
113 Servers []ServerDetail | 163 Servers []ServerDetail |
114 } | 164 } |
115 » requestData := goosehttp.RequestData{RespValue: &resp} | 165 » requestData := goosehttp.RequestData{RespValue: &resp, Params: &filter.V alues} |
116 err := c.client.SendRequest(client.GET, "compute", apiServersDetail, &re questData, | 166 err := c.client.SendRequest(client.GET, "compute", apiServersDetail, &re questData, |
117 "failed to get list of servers details") | 167 "failed to get list of servers details") |
118 if err != nil { | 168 if err != nil { |
119 return nil, err | 169 return nil, err |
120 } | 170 } |
121 » return &resp.Servers, nil | 171 » return resp.Servers, nil |
122 } | 172 } |
123 | 173 |
124 // GetServer lists details for the specified server. | 174 // GetServer lists details for the specified server. |
125 func (c *Client) GetServer(serverId string) (*ServerDetail, error) { | 175 func (c *Client) GetServer(serverId string) (*ServerDetail, error) { |
126 var resp struct { | 176 var resp struct { |
127 Server ServerDetail | 177 Server ServerDetail |
128 } | 178 } |
129 url := fmt.Sprintf("%s/%s", apiServers, serverId) | 179 url := fmt.Sprintf("%s/%s", apiServers, serverId) |
130 requestData := goosehttp.RequestData{RespValue: &resp} | 180 requestData := goosehttp.RequestData{RespValue: &resp} |
131 err := c.client.SendRequest(client.GET, "compute", url, &requestData, | 181 err := c.client.SendRequest(client.GET, "compute", url, &requestData, |
(...skipping 20 matching lines...) Expand all Loading... | |
152 Name string `json:"name"` | 202 Name string `json:"name"` |
153 FlavorId string `json:"flavorRef"` | 203 FlavorId string `json:"flavorRef"` |
154 ImageId string `json:"imageRef"` | 204 ImageId string `json:"imageRef"` |
155 UserData *string `json:"user_data"` | 205 UserData *string `json:"user_data"` |
156 SecurityGroupNames []struct { | 206 SecurityGroupNames []struct { |
157 Name string `json:"name"` | 207 Name string `json:"name"` |
158 } `json:"security_groups"` | 208 } `json:"security_groups"` |
159 } | 209 } |
160 | 210 |
161 // RunServer creates a new server. | 211 // RunServer creates a new server. |
162 func (c *Client) RunServer(opts RunServerOpts) error { | 212 func (c *Client) RunServer(opts RunServerOpts) (*Entity, error) { |
163 var req struct { | 213 var req struct { |
164 Server RunServerOpts `json:"server"` | 214 Server RunServerOpts `json:"server"` |
165 } | 215 } |
166 req.Server = opts | 216 req.Server = opts |
167 if opts.UserData != nil { | 217 if opts.UserData != nil { |
168 data := []byte(*opts.UserData) | 218 data := []byte(*opts.UserData) |
169 encoded := base64.StdEncoding.EncodeToString(data) | 219 encoded := base64.StdEncoding.EncodeToString(data) |
170 req.Server.UserData = &encoded | 220 req.Server.UserData = &encoded |
171 } | 221 } |
172 » requestData := goosehttp.RequestData{ReqValue: req, ExpectedStatus: []in t{http.StatusAccepted}} | 222 » var resp struct { |
223 » » Server Entity `json:"server"` | |
224 » } | |
225 » requestData := goosehttp.RequestData{ReqValue: req, RespValue: &resp, Ex pectedStatus: []int{http.StatusAccepted}} | |
173 err := c.client.SendRequest(client.POST, "compute", apiServers, &request Data, | 226 err := c.client.SendRequest(client.POST, "compute", apiServers, &request Data, |
174 "failed to run a server with %#v", opts) | 227 "failed to run a server with %#v", opts) |
175 » return err | 228 » if err != nil { |
229 » » return nil, err | |
230 » } | |
231 » return &resp.Server, nil | |
176 } | 232 } |
177 | 233 |
178 type SecurityGroupRule struct { | 234 type SecurityGroupRule struct { |
179 FromPort *int `json:"from_port"` // Can be nil | 235 FromPort *int `json:"from_port"` // Can be nil |
180 IPProtocol *string `json:"ip_protocol"` // Can be nil | 236 IPProtocol *string `json:"ip_protocol"` // Can be nil |
181 ToPort *int `json:"to_port"` // Can be nil | 237 ToPort *int `json:"to_port"` // Can be nil |
182 ParentGroupId int `json:"parent_group_id"` | 238 ParentGroupId int `json:"parent_group_id"` |
183 IPRange map[string]string `json:"ip_range"` // Can be empty | 239 IPRange map[string]string `json:"ip_range"` // Can be empty |
184 Id int | 240 Id int |
185 Group map[string]string // Can be empty | 241 Group map[string]string // Can be empty |
186 } | 242 } |
187 | 243 |
188 type SecurityGroup struct { | 244 type SecurityGroup struct { |
189 Rules []SecurityGroupRule | 245 Rules []SecurityGroupRule |
190 TenantId string `json:"tenant_id"` | 246 TenantId string `json:"tenant_id"` |
191 Id int | 247 Id int |
192 Name string | 248 Name string |
193 Description string | 249 Description string |
194 } | 250 } |
195 | 251 |
196 // ListSecurityGroups lists IDs, names, and other details for all security group s. | 252 // ListSecurityGroups lists IDs, names, and other details for all security group s. |
197 func (c *Client) ListSecurityGroups() (*[]SecurityGroup, error) { | 253 func (c *Client) ListSecurityGroups() ([]SecurityGroup, error) { |
198 var resp struct { | 254 var resp struct { |
199 Groups []SecurityGroup `json:"security_groups"` | 255 Groups []SecurityGroup `json:"security_groups"` |
200 } | 256 } |
201 requestData := goosehttp.RequestData{RespValue: &resp} | 257 requestData := goosehttp.RequestData{RespValue: &resp} |
202 err := c.client.SendRequest(client.GET, "compute", apiSecurityGroups, &r equestData, | 258 err := c.client.SendRequest(client.GET, "compute", apiSecurityGroups, &r equestData, |
203 "failed to list security groups") | 259 "failed to list security groups") |
204 if err != nil { | 260 if err != nil { |
205 return nil, err | 261 return nil, err |
206 } | 262 } |
207 » return &resp.Groups, nil | 263 » return resp.Groups, nil |
208 } | 264 } |
209 | 265 |
210 // GetServerSecurityGroups list security groups for a specific server. | 266 // GetServerSecurityGroups list security groups for a specific server. |
211 func (c *Client) GetServerSecurityGroups(serverId string) (*[]SecurityGroup, err or) { | 267 func (c *Client) GetServerSecurityGroups(serverId string) ([]SecurityGroup, erro r) { |
212 | 268 |
213 var resp struct { | 269 var resp struct { |
214 Groups []SecurityGroup `json:"security_groups"` | 270 Groups []SecurityGroup `json:"security_groups"` |
215 } | 271 } |
216 url := fmt.Sprintf("%s/%s/%s", apiServers, serverId, apiSecurityGroups) | 272 url := fmt.Sprintf("%s/%s/%s", apiServers, serverId, apiSecurityGroups) |
217 requestData := goosehttp.RequestData{RespValue: &resp} | 273 requestData := goosehttp.RequestData{RespValue: &resp} |
218 err := c.client.SendRequest(client.GET, "compute", url, &requestData, | 274 err := c.client.SendRequest(client.GET, "compute", url, &requestData, |
219 "failed to list server (%s) security groups", serverId) | 275 "failed to list server (%s) security groups", serverId) |
220 if err != nil { | 276 if err != nil { |
221 return nil, err | 277 return nil, err |
222 } | 278 } |
223 » return &resp.Groups, nil | 279 » return resp.Groups, nil |
224 } | 280 } |
225 | 281 |
226 // CreateSecurityGroup creates a new security group. | 282 // CreateSecurityGroup creates a new security group. |
227 func (c *Client) CreateSecurityGroup(name, description string) (*SecurityGroup, error) { | 283 func (c *Client) CreateSecurityGroup(name, description string) (*SecurityGroup, error) { |
228 var req struct { | 284 var req struct { |
229 SecurityGroup struct { | 285 SecurityGroup struct { |
230 Name string `json:"name"` | 286 Name string `json:"name"` |
231 Description string `json:"description"` | 287 Description string `json:"description"` |
232 } `json:"security_group"` | 288 } `json:"security_group"` |
233 } | 289 } |
(...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
327 | 383 |
328 type FloatingIP struct { | 384 type FloatingIP struct { |
329 FixedIP interface{} `json:"fixed_ip"` // Can be a string or null | 385 FixedIP interface{} `json:"fixed_ip"` // Can be a string or null |
330 Id int `json:"id"` | 386 Id int `json:"id"` |
331 InstanceId interface{} `json:"instance_id"` // Can be a string or null | 387 InstanceId interface{} `json:"instance_id"` // Can be a string or null |
332 IP string `json:"ip"` | 388 IP string `json:"ip"` |
333 Pool string `json:"pool"` | 389 Pool string `json:"pool"` |
334 } | 390 } |
335 | 391 |
336 // ListFloatingIPs lists floating IP addresses associated with the tenant or acc ount. | 392 // ListFloatingIPs lists floating IP addresses associated with the tenant or acc ount. |
337 func (c *Client) ListFloatingIPs() (*[]FloatingIP, error) { | 393 func (c *Client) ListFloatingIPs() ([]FloatingIP, error) { |
338 var resp struct { | 394 var resp struct { |
339 FloatingIPs []FloatingIP `json:"floating_ips"` | 395 FloatingIPs []FloatingIP `json:"floating_ips"` |
340 } | 396 } |
341 | 397 |
342 requestData := goosehttp.RequestData{RespValue: &resp} | 398 requestData := goosehttp.RequestData{RespValue: &resp} |
343 err := c.client.SendRequest(client.GET, "compute", apiFloatingIPs, &requ estData, | 399 err := c.client.SendRequest(client.GET, "compute", apiFloatingIPs, &requ estData, |
344 "failed to list floating ips") | 400 "failed to list floating ips") |
345 if err != nil { | 401 if err != nil { |
346 return nil, err | 402 return nil, err |
347 } | 403 } |
348 » return &resp.FloatingIPs, nil | 404 » return resp.FloatingIPs, nil |
349 } | 405 } |
350 | 406 |
351 // GetFloatingIP lists details of the floating IP address associated with specif ied id. | 407 // GetFloatingIP lists details of the floating IP address associated with specif ied id. |
352 func (c *Client) GetFloatingIP(ipId int) (*FloatingIP, error) { | 408 func (c *Client) GetFloatingIP(ipId int) (*FloatingIP, error) { |
353 var resp struct { | 409 var resp struct { |
354 FloatingIP FloatingIP `json:"floating_ip"` | 410 FloatingIP FloatingIP `json:"floating_ip"` |
355 } | 411 } |
356 | 412 |
357 url := fmt.Sprintf("%s/%d", apiFloatingIPs, ipId) | 413 url := fmt.Sprintf("%s/%d", apiFloatingIPs, ipId) |
358 requestData := goosehttp.RequestData{RespValue: &resp} | 414 requestData := goosehttp.RequestData{RespValue: &resp} |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
412 } `json:"removeFloatingIp"` | 468 } `json:"removeFloatingIp"` |
413 } | 469 } |
414 req.RemoveFloatingIP.Address = address | 470 req.RemoveFloatingIP.Address = address |
415 | 471 |
416 url := fmt.Sprintf("%s/%s/action", apiServers, serverId) | 472 url := fmt.Sprintf("%s/%s/action", apiServers, serverId) |
417 requestData := goosehttp.RequestData{ReqValue: req, ExpectedStatus: []in t{http.StatusAccepted}} | 473 requestData := goosehttp.RequestData{ReqValue: req, ExpectedStatus: []in t{http.StatusAccepted}} |
418 err := c.client.SendRequest(client.POST, "compute", url, &requestData, | 474 err := c.client.SendRequest(client.POST, "compute", url, &requestData, |
419 "failed to remove floating ip %s to server %s", address, serverI d) | 475 "failed to remove floating ip %s to server %s", address, serverI d) |
420 return err | 476 return err |
421 } | 477 } |
OLD | NEW |