Ejemplo n.º 1
0
// 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

}
Ejemplo n.º 2
0
Archivo: store.go Proyecto: romana/core
// 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.")

}