Merge branch 'master' into v0.2.0
diff --git a/api_fetch.go b/api_fetch.go
new file mode 100644
index 0000000..54ca9be
--- /dev/null
+++ b/api_fetch.go
@@ -0,0 +1,42 @@
+package gophercloud
+
+import(
+ "github.com/mitchellh/mapstructure"
+)
+
+//The default generic openstack api
+var OpenstackApi = map[string]interface{}{
+	"UrlChoice": PublicURL,
+}
+
+// Api for use with rackspace
+var RackspaceApi = map[string]interface{}{
+	"Name":      "cloudServersOpenStack",
+	"VersionId": "2",
+	"UrlChoice": PublicURL,
+}
+
+
+//Populates an ApiCriteria struct with the api values
+//from one of the api maps 
+func PopulateApi(variant string) (ApiCriteria, error){
+	var Api ApiCriteria
+	var variantMap map[string]interface{}
+
+	switch variant {
+	case "":
+		variantMap = OpenstackApi
+
+	case "openstack":
+		variantMap = OpenstackApi
+
+	case "rackspace": 
+		variantMap = RackspaceApi
+	}
+
+	err := mapstructure.Decode(variantMap,&Api)
+		if err != nil{
+			return Api,err
+		}
+	return Api, err 
+}
diff --git a/floating_ips.go b/floating_ips.go
index 4c27347..1163667 100644
--- a/floating_ips.go
+++ b/floating_ips.go
@@ -1,6 +1,7 @@
 package gophercloud
 
 import (
+	"errors"
 	"fmt"
 	"github.com/racker/perigee"
 )
@@ -24,7 +25,7 @@
 }
 
 func (gsp *genericServersProvider) CreateFloatingIp(pool string) (FloatingIp, error) {
-	var fip *FloatingIp
+	fip := new(FloatingIp)
 
 	err := gsp.context.WithReauth(gsp.access, func() error {
 		url := gsp.endpoint + "/os-floating-ips"
@@ -42,6 +43,10 @@
 		})
 	})
 
+	if fip.Ip == "" {
+		return *fip, errors.New("Error creating floating IP")
+	}
+
 	return *fip, err
 }
 
@@ -63,7 +68,7 @@
 
 func (gsp *genericServersProvider) DeleteFloatingIp(ip FloatingIp) error {
 	return gsp.context.WithReauth(gsp.access, func() error {
-		ep := fmt.Sprintf("%s/os-floating-ips/%s", gsp.endpoint, ip.Id)
+		ep := fmt.Sprintf("%s/os-floating-ips/%d", gsp.endpoint, ip.Id)
 		return perigee.Delete(ep, perigee.Options{
 			CustomClient: gsp.context.httpClient,
 			MoreHeaders: map[string]string{
@@ -75,7 +80,7 @@
 }
 
 type FloatingIp struct {
-	Id         string `json:"id"`
+	Id         int    `json:"id"`
 	Pool       string `json:"pool"`
 	Ip         string `json:"ip"`
 	FixedIp    string `json:"fixed_ip"`
diff --git a/interfaces.go b/interfaces.go
index 6340c1c..b038aa7 100644
--- a/interfaces.go
+++ b/interfaces.go
@@ -193,4 +193,25 @@
 
 	// ShowKeyPair will yield the named keypair.
 	ShowKeyPair(name string) (KeyPair, error)
+
+	// ListSecurityGroups provides a listing of security groups for the tenant.
+	// This method works only if the provider supports the os-security-groups extension.
+	ListSecurityGroups() ([]SecurityGroup, error)
+
+	// CreateSecurityGroup lets a tenant create a new security group.
+	// Only the SecurityGroup fields which are specified will be marshalled to the API.
+	// This method works only if the provider supports the os-security-groups extension.
+	CreateSecurityGroup(desired SecurityGroup) (*SecurityGroup, error)
+
+	// ListSecurityGroupsByServerId provides a list of security groups which apply to the indicated server.
+	// This method works only if the provider supports the os-security-groups extension.
+	ListSecurityGroupsByServerId(id string) ([]SecurityGroup, error)
+
+	// SecurityGroupById returns a security group corresponding to the provided ID number.
+	// This method works only if the provider supports the os-security-groups extension.
+	SecurityGroupById(id int) (*SecurityGroup, error)
+
+	// DeleteSecurityGroupById disposes of a security group corresponding to the provided ID number.
+	// This method works only if the provider supports the os-security-groups extension.
+	DeleteSecurityGroupById(id int) error
 }
diff --git a/servers.go b/servers.go
index fc5925d..57f1532 100644
--- a/servers.go
+++ b/servers.go
@@ -369,6 +369,114 @@
 	return locationArr[len(locationArr)-1], err
 }
 
+// See the CloudServersProvider interface for details.
+func (gsp *genericServersProvider) ListSecurityGroups() ([]SecurityGroup, error) {
+	var sgs []SecurityGroup
+
+	err := gsp.context.WithReauth(gsp.access, func() error {
+		ep := fmt.Sprintf("%s/os-security-groups", gsp.endpoint)
+		return perigee.Get(ep, perigee.Options{
+			MoreHeaders: map[string]string{
+				"X-Auth-Token": gsp.access.AuthToken(),
+			},
+			Results: &struct {
+				SecurityGroups *[]SecurityGroup `json:"security_groups"`
+			}{&sgs},
+		})
+	})
+	return sgs, err
+}
+
+// See the CloudServersProvider interface for details.
+func (gsp *genericServersProvider) CreateSecurityGroup(desired SecurityGroup) (*SecurityGroup, error) {
+	var actual *SecurityGroup
+
+	err := gsp.context.WithReauth(gsp.access, func() error {
+		ep := fmt.Sprintf("%s/os-security-groups", gsp.endpoint)
+		return perigee.Post(ep, perigee.Options{
+			ReqBody: struct {
+				AddSecurityGroup SecurityGroup `json:"security_group"`
+			}{desired},
+			MoreHeaders: map[string]string{
+				"X-Auth-Token": gsp.access.AuthToken(),
+			},
+			Results: &struct {
+				SecurityGroup **SecurityGroup `json:"security_group"`
+			}{&actual},
+		})
+	})
+	return actual, err
+}
+
+// See the CloudServersProvider interface for details.
+func (gsp *genericServersProvider) ListSecurityGroupsByServerId(id string) ([]SecurityGroup, error) {
+	var sgs []SecurityGroup
+
+	err := gsp.context.WithReauth(gsp.access, func() error {
+		ep := fmt.Sprintf("%s/servers/%s/os-security-groups", gsp.endpoint, id)
+		return perigee.Get(ep, perigee.Options{
+			MoreHeaders: map[string]string{
+				"X-Auth-Token": gsp.access.AuthToken(),
+			},
+			Results: &struct {
+				SecurityGroups *[]SecurityGroup `json:"security_groups"`
+			}{&sgs},
+		})
+	})
+	return sgs, err
+}
+
+// See the CloudServersProvider interface for details.
+func (gsp *genericServersProvider) SecurityGroupById(id int) (*SecurityGroup, error) {
+	var actual *SecurityGroup
+
+	err := gsp.context.WithReauth(gsp.access, func() error {
+		ep := fmt.Sprintf("%s/os-security-groups/%d", gsp.endpoint, id)
+		return perigee.Get(ep, perigee.Options{
+			MoreHeaders: map[string]string{
+				"X-Auth-Token": gsp.access.AuthToken(),
+			},
+			Results: &struct {
+				SecurityGroup **SecurityGroup `json:"security_group"`
+			}{&actual},
+		})
+	})
+	return actual, err
+}
+
+// See the CloudServersProvider interface for details.
+func (gsp *genericServersProvider) DeleteSecurityGroupById(id int) error {
+	err := gsp.context.WithReauth(gsp.access, func() error {
+		ep := fmt.Sprintf("%s/os-security-groups/%d", gsp.endpoint, id)
+		return perigee.Delete(ep, perigee.Options{
+			MoreHeaders: map[string]string{
+				"X-Auth-Token": gsp.access.AuthToken(),
+			},
+			OkCodes: []int{202},
+		})
+	})
+	return err
+}
+
+// SecurityGroup provides a description of a security group, including all its rules.
+type SecurityGroup struct {
+	Description string   `json:"description,omitempty"`
+	Id          int      `json:"id,omitempty"`
+	Name        string   `json:"name,omitempty"`
+	Rules       []SGRule `json:"rules,omitempty"`
+	TenantId    string   `json:"tenant_id,omitempty"`
+}
+
+// SGRule encapsulates a single rule which applies to a security group.
+// This definition is just a guess, based on the documentation found in another extension here: http://docs.openstack.org/api/openstack-compute/2/content/GET_os-security-group-default-rules-v2_listSecGroupDefaultRules_v2__tenant_id__os-security-group-rules_ext-os-security-group-default-rules.html
+type SGRule struct {
+	FromPort   int                    `json:"from_port,omitempty"`
+	Id         int                    `json:"id,omitempty"`
+	IpProtocol string                 `json:"ip_protocol,omitempty"`
+	IpRange    map[string]interface{} `json:"ip_range,omitempty"`
+	ToPort     int                    `json:"to_port,omitempty"`
+}
+
 // RaxBandwidth provides measurement of server bandwidth consumed over a given audit interval.
 type RaxBandwidth struct {
 	AuditPeriodEnd    string `json:"audit_period_end"`