// handleHost handles request for a specific host's info func (ipam *IPAMSvc) addVm(input interface{}, ctx common.RestContext) (interface{}, error) { vm := input.(*Vm) err := ipam.store.addVm(ipam.dc.EndpointSpaceBits, vm) if err != nil { return nil, err } client, err := common.NewRestClient("", ipam.config.Common.Api.RestTimeoutMillis) if err != nil { return nil, err } // Get host info from topology service topoUrl, err := client.GetServiceUrl(ipam.config.Common.Api.RootServiceUrl, "topology") if err != nil { return nil, err } index := common.IndexResponse{} err = client.Get(topoUrl, &index) if err != nil { return nil, err } hostsUrl := index.Links.FindByRel("host-list") host := common.HostMessage{} hostInfoUrl := fmt.Sprintf("%s/%s", hostsUrl, vm.HostId) err = client.Get(hostInfoUrl, &host) if err != nil { return nil, err } tenantUrl, err := client.GetServiceUrl(ipam.config.Common.Api.RootServiceUrl, "tenant") if err != nil { return nil, err } // TODO follow links once tenant service supports it. For now... t := &tenant.Tenant{} tenantsUrl := fmt.Sprintf("%s/tenants/%s", tenantUrl, vm.TenantId) log.Printf("IPAM calling %s\n", tenantsUrl) err = client.Get(tenantsUrl, t) if err != nil { return nil, err } log.Printf("IPAM received tenant %s ID %d\n", t.Name, t.Id) segmentUrl := fmt.Sprintf("/tenants/%s/segments/%s", vm.TenantId, vm.SegmentId) log.Printf("IPAM calling %s\n", segmentUrl) segment := &tenant.Segment{} err = client.Get(segmentUrl, segment) if err != nil { return nil, err } log.Printf("Constructing IP from Host IP %s, Tenant %d, Segment %d", host.RomanaIp, t.Seq, segment.Seq) vmBits := 32 - ipam.dc.PrefixBits - ipam.dc.PortBits - ipam.dc.TenantBits - ipam.dc.SegmentBits - ipam.dc.EndpointSpaceBits segmentBitShift := vmBits prefixBitShift := 32 - ipam.dc.PrefixBits tenantBitShift := segmentBitShift + ipam.dc.SegmentBits // hostBitShift := tenantBitShift + ipam.dc.TenantBits log.Printf("Parsing Romana IP address of host %s: %s\n", host.Name, host.RomanaIp) hostIp, _, err := net.ParseCIDR(host.RomanaIp) if err != nil { return nil, err } hostIpInt := common.IPv4ToInt(hostIp) vmIpInt := (ipam.dc.Prefix << prefixBitShift) | hostIpInt | (t.Seq << tenantBitShift) | (segment.Seq << segmentBitShift) | vm.EffectiveSeq vmIpIp := common.IntToIPv4(vmIpInt) log.Printf("Constructing (%d << %d) | %d | (%d << %d) | ( %d << %d ) | %d=%s\n", ipam.dc.Prefix, prefixBitShift, hostIpInt, t.Seq, tenantBitShift, segment.Seq, segmentBitShift, vm.EffectiveSeq, vmIpIp.String()) vm.Ip = vmIpIp.String() return vm, nil }
// addEndpoint allocates an IP address and stores it in the // database. func (ipamStore *ipamStore) addEndpoint(endpoint *Endpoint, upToEndpointIpInt uint64, dc common.Datacenter) error { var err error tx := ipamStore.DbStore.Db.Begin() if endpoint.RequestToken.Valid && endpoint.RequestToken.String != "" { var existingEndpoints []Endpoint var count int tx.Where("request_token = ?", endpoint.RequestToken.String).Find(&existingEndpoints).Count(&count) err = common.GetDbErrors(tx) if err != nil { log.Printf("IPAM Errors 1: %v", err) tx.Rollback() return err } if count > 0 { // This will only be 1, because of unique constraint. tx.Rollback() log.Printf("Found existing %s: %+v", endpoint.RequestToken.String, existingEndpoints[0]) endpoint.EffectiveNetworkID = existingEndpoints[0].EffectiveNetworkID endpoint.HostId = existingEndpoints[0].HostId endpoint.Id = existingEndpoints[0].Id endpoint.InUse = existingEndpoints[0].InUse endpoint.Name = existingEndpoints[0].Name endpoint.NetworkID = existingEndpoints[0].NetworkID endpoint.RequestToken = existingEndpoints[0].RequestToken endpoint.SegmentID = existingEndpoints[0].SegmentID endpoint.TenantID = existingEndpoints[0].TenantID endpoint.Ip = existingEndpoints[0].Ip return nil } } hostId := endpoint.HostId endpoint.InUse = true tenantId := endpoint.TenantID segId := endpoint.SegmentID filter := "host_id = ? AND tenant_id = ? AND segment_id = ? " var sel string // First, find the MAX network ID available for this host/segment combination. sel = "IFNULL(MAX(network_id),-1)+1" log.Printf("IpamStore: Calling SELECT %s FROM endpoints WHERE %s;", sel, fmt.Sprintf(strings.Replace(filter, "?", "%s", 3), hostId, tenantId, segId)) row := tx.Model(Endpoint{}).Where(filter, hostId, tenantId, segId).Select(sel).Row() err = common.GetDbErrors(tx) if err != nil { log.Printf("IPAM Errors 2: %v", err) tx.Rollback() return err } netID := sql.NullInt64{} row.Scan(&netID) err = common.GetDbErrors(tx) if err != nil { log.Printf("IPAM Errors 3: %v", err) tx.Rollback() return err } log.Printf("IpamStore: max net ID: %v", netID) maxEffNetID := uint64(1<<(dc.EndpointSpaceBits+dc.EndpointBits) - 1) // Does this exceed max bits? endpoint.NetworkID = uint64(netID.Int64) endpoint.EffectiveNetworkID = getEffectiveNetworkID(endpoint.NetworkID, dc.EndpointSpaceBits) if endpoint.EffectiveNetworkID <= maxEffNetID { // Does not exceed max bits, all good. // log.Printf("IpamStore: Effective network ID for network ID %d (stride %d): %d\n", endpoint.NetworkID, dc.EndpointSpaceBits, endpoint.EffectiveNetworkID) ipInt := upToEndpointIpInt | endpoint.EffectiveNetworkID // log.Printf("IpamStore: %d | %d = %d", upToEndpointIpInt, endpoint.EffectiveNetworkID, ipInt) endpoint.Ip = common.IntToIPv4(ipInt).String() tx = tx.Create(endpoint) err = common.GetDbErrors(tx) if err != nil { log.Printf("IPAM Errors 4: %v", err) tx.Rollback() return err } log.Printf("IpamStore: Allocated %d: %s", endpoint.NetworkID, endpoint.Ip) tx.Commit() return nil } // Out of bits, see if we can reuse an earlier allocated address... log.Printf("IpamStore: New effective network ID is %d, exceeds maximum %d\n", endpoint.EffectiveNetworkID, maxEffNetID) // See if there is a formerly allocated IP already that has been released // (marked "in_use") sel = "MIN(network_id), ip" log.Printf("IpamStore: Calling SELECT %s FROM endpoints WHERE %s;", sel, fmt.Sprintf(strings.Replace(filter+"AND in_use = 0", "?", "%s", 3), hostId, tenantId, segId)) // In containerized setup, not using group by leads to failure due to // incompatible sql mode, thus use "GROUP BY network_id, ip" to avoid // this failure. row = tx.Model(Endpoint{}).Where(filter+"AND in_use = 0", hostId, tenantId, segId).Select(sel).Group("ip").Order("MIN(network_id) ASC").Row() err = common.GetDbErrors(tx) if err != nil { log.Printf("IPAM Errors 5: %v", err) tx.Rollback() return err } netID = sql.NullInt64{} var ip string row.Scan(&netID, &ip) err = common.GetDbErrors(tx) if err != nil { log.Printf("IPAM Errors 6: %v", err) tx.Rollback() return err } if netID.Valid { log.Printf("IpamStore: Reusing %d: %s", netID.Int64, ip) endpoint.Ip = ip tx = tx.Model(Endpoint{}).Where("ip = ?", ip).Update("in_use", true) err = common.GetDbErrors(tx) if err != nil { log.Printf("IPAM Errors 7: %v", err) tx.Rollback() return err } tx.Commit() return nil } tx.Rollback() return common.NewError("Out of IP addresses.") }