Example #1
0
// Scan implements driver.Scanner interface on IP
func (i *IP) Scan(src interface{}) error {
	switch src := src.(type) {
	case string:
		ip := net.ParseIP(src)
		if ip == nil {
			return common.NewError("Cannot parse IP %s", src)
		}
		i.IP = ip
		return nil
	case []uint8:
		i.IP = net.ParseIP(string(src))
		return nil
	default:
		return common.NewError("Incompatible type for IP")
	}
}
Example #2
0
// SetConfig implements SetConfig function of the Service interface.
// Returns an error if cannot connect to the data store
func (topology *TopologySvc) SetConfig(config common.ServiceConfig) error {
	topology.config = config
	dcMap := config.ServiceSpecific["datacenter"].(map[string]interface{})
	dc := common.Datacenter{}
	dc.IpVersion = uint(dcMap["ip_version"].(float64))
	if dc.IpVersion != 4 {
		return common.NewError("Only IPv4 is currently supported.")
	}
	dc.Cidr = dcMap["cidr"].(string)
	_, ipNet, err := net.ParseCIDR(dc.Cidr)
	if err != nil {
		return err
	}
	prefixBits, _ := ipNet.Mask.Size()
	dc.PrefixBits = uint(prefixBits)

	dc.PortBits = uint(dcMap["host_bits"].(float64))
	dc.TenantBits = uint(dcMap["tenant_bits"].(float64))
	dc.SegmentBits = uint(dcMap["segment_bits"].(float64))
	dc.EndpointBits = uint(dcMap["endpoint_bits"].(float64))
	dc.EndpointSpaceBits = uint(dcMap["endpoint_space_bits"].(float64))
	if dc.EndpointBits == 0 {
		return common.NewError("Endpoint bits may not be 0")
	}
	bitSum := dc.PrefixBits + dc.PortBits + dc.TenantBits + dc.SegmentBits + dc.EndpointBits + dc.EndpointSpaceBits
	if bitSum != 32 {
		bitSumStr := fmt.Sprintf("%s+%d+%d+%d+%d+%d", dc.Cidr, dc.PortBits, dc.TenantBits, dc.SegmentBits, dc.EndpointBits, dc.EndpointSpaceBits)
		return common.NewError("Sum of prefix, port, tenant, segment, endpoint and endpoint space bits must be exactly 32, but it is %s=%d", bitSumStr, bitSum)
	}

	// TODO this should have worked but it doesn't...
	//	err := mapstructure.Decode(dcMap, &dc)
	//	if err != nil {
	//		return err
	//	}
	log.Printf("Datacenter information: was %s, decoded to %+v\n", dcMap, dc)
	topology.datacenter = &dc
	storeConfig := config.ServiceSpecific["store"].(map[string]interface{})
	topology.store = topoStore{}
	topology.store.ServiceStore = &topology.store
	return topology.store.SetConfig(storeConfig)
}
Example #3
0
// translateTarget analizes kubePolicy and fills romanaPolicy.AppliedTo field.
func (tg *TranslateGroup) translateTarget(translator *Translator) error {

	// Translate kubernetes namespace into romana tenant. Must be defined.
	tenantCacheEntry := translator.checkTenantInCache(tg.kubePolicy.ObjectMeta.Namespace)
	if tenantCacheEntry == nil {
		log.Errorf("Tenant not found when translating policy %v", tg.romanaPolicy)
		return TranslatorError{ErrorTenantNotInCache, nil}
	}

	// Empty PodSelector means policy applied to the entire namespace.
	if len(tg.kubePolicy.Spec.PodSelector.MatchLabels) == 0 {
		tg.romanaPolicy.AppliedTo = []common.Endpoint{
			common.Endpoint{TenantID: tenantCacheEntry.Tenant.ID, TenantExternalID: tenantCacheEntry.Tenant.ExternalID},
		}

		log.Tracef(trace.Inside, "Segment was not specified in policy %v, assuming target is a namespace", tg.kubePolicy)
		return nil
	}

	// If PodSelector is not empty then segment label must be defined.
	kubeSegmentID, ok := tg.kubePolicy.Spec.PodSelector.MatchLabels[translator.segmentLabelName]
	if !ok || kubeSegmentID == "" {
		log.Errorf("Expected segment to be specified in podSelector part as %s", translator.segmentLabelName)
		return common.NewError("Expected segment to be specified in podSelector part as '%s'", translator.segmentLabelName)
	}

	// Translate kubernetes segment label into romana segment.
	segment, err := translator.getOrAddSegment(tg.kubePolicy.ObjectMeta.Namespace, kubeSegmentID)
	if err != nil {
		log.Errorf("Error in translate while calling l.getOrAddSegment with %s and %s - error %s", tg.kubePolicy.ObjectMeta.Namespace, kubeSegmentID, err)
		return err
	}

	tg.romanaPolicy.AppliedTo = []common.Endpoint{
		common.Endpoint{TenantID: tenantCacheEntry.Tenant.ID, TenantExternalID: tenantCacheEntry.Tenant.ExternalID, SegmentID: segment.ID},
	}

	return nil
}
Example #4
0
// deleteIPtablesRulesBySubstring uninstalls iptables Rules matching given
// substring and deletes them from database. Has no effect on 'inactive' Rules.
func (fw *IPtables) deleteIPtablesRulesBySubstring(substring string) error {
	if substring == "" {
		return common.NewError("Empty substring specified to deleteIPtablesRulesBySubstring")
	}
	rules, err := fw.Store.findIPtablesRules(substring)
	if err != nil {
		return err
	}

	for _, rule := range *rules {
		if rule.State == setRuleInactive.String() {
			continue
		}

		err = fw.deleteIPtablesRule(&rule)
		if err != nil {
			return err
		}
	}

	return nil
}
Example #5
0
/// makeNextIngressPeer analyzes current Ingress rule and adds new Peer to romanaPolicy.Peers.
func (tg *TranslateGroup) makeNextIngressPeer(translator *Translator) error {
	ingress := tg.kubePolicy.Spec.Ingress[tg.ingressIndex]

	for _, fromEntry := range ingress.From {
		tenantCacheEntry := &TenantCacheEntry{}

		// Exactly one of From.PodSelector or From.NamespaceSelector must be specified.
		if fromEntry.PodSelector == nil && fromEntry.NamespaceSelector == nil {
			log.Errorf("Either PodSElector or NamespacesSelector must be specified")
			return common.NewError("Either PodSElector or NamespacesSelector must be specified")
		} else if fromEntry.PodSelector != nil && fromEntry.NamespaceSelector != nil {
			log.Errorf("Exactly one of PodSElector or NamespacesSelector must be specified")
			return common.NewError("Exactly on of PodSElector or NamespacesSelector must be specified")
		}

		// This ingress field matching a namespace which will be our source tenant.
		if fromEntry.NamespaceSelector != nil {
			tenantName, ok := fromEntry.NamespaceSelector.MatchLabels[translator.tenantLabelName]
			if !ok || tenantName == "" {
				log.Errorf("Expected tenant name to be specified in NamespaceSelector field with a key %s", translator.tenantLabelName)
				return common.NewError("Expected tenant name to be specified in NamespaceSelector field with a key %s", translator.tenantLabelName)
			}

			tenantCacheEntry = translator.checkTenantInCache(tenantName)
			if tenantCacheEntry == nil {
				log.Errorf("Tenant not not found when translating policy %v", tg.romanaPolicy)
				return TranslatorError{ErrorTenantNotInCache, nil}
			}

			// Found a source tenant, let's register it as romana Peeer.
			tg.romanaPolicy.Ingress[tg.ingressIndex].Peers = append(tg.romanaPolicy.Ingress[tg.ingressIndex].Peers,
				common.Endpoint{TenantID: tenantCacheEntry.Tenant.ID, TenantExternalID: tenantCacheEntry.Tenant.ExternalID})
		}

		// This ingress field matches a segment and source tenant is a same as target tenant.
		if fromEntry.PodSelector != nil {

			// Check if source/target tenant in cache.
			tenantCacheEntry = translator.checkTenantInCache(tg.kubePolicy.ObjectMeta.Namespace)
			if tenantCacheEntry == nil {
				log.Errorf("Tenant not not found when translating policy %v", tg.romanaPolicy)
				return TranslatorError{ErrorTenantNotInCache, nil}
			}

			// If podSelector is empty match all traffic from the tenant.
			if len(fromEntry.PodSelector.MatchLabels) == 0 {
				tg.romanaPolicy.Ingress[tg.ingressIndex].Peers = append(tg.romanaPolicy.Ingress[tg.ingressIndex].Peers,
					common.Endpoint{TenantID: tenantCacheEntry.Tenant.ID, TenantExternalID: tenantCacheEntry.Tenant.ExternalID})

				log.Tracef(trace.Inside, "No segment specified when translating ingress rule %v", tg.kubePolicy.Spec.Ingress[tg.ingressIndex])
				return nil
			}

			// Get segment name from podSelector.
			kubeSegmentID, ok := fromEntry.PodSelector.MatchLabels[translator.segmentLabelName]
			if !ok || kubeSegmentID == "" {
				log.Errorf("Expected segment to be specified in podSelector part as %s", translator.segmentLabelName)
				return common.NewError("Expected segment to be specified in podSelector part as '%s'", translator.segmentLabelName)
			}

			// Translate kubernetes segment name into romana segment.
			segment, err := translator.getOrAddSegment(tenantCacheEntry.Tenant.Name, kubeSegmentID)
			if err != nil {
				log.Errorf("Error in translate while calling l.getOrAddSegment with %s and %s - error %s", tenantCacheEntry.Tenant.Name, kubeSegmentID, err)
				return err
			}

			// Register source tenant/segment as a romana Peer.
			tg.romanaPolicy.Ingress[tg.ingressIndex].Peers = append(tg.romanaPolicy.Ingress[tg.ingressIndex].Peers,
				common.Endpoint{TenantID: tenantCacheEntry.Tenant.ID, TenantExternalID: tenantCacheEntry.Tenant.ExternalID, SegmentID: segment.ID})
		}

	}
	return nil
}
Example #6
0
File: store.go Project: 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.")

}