def wrapper(*args, **kwargs): if config.parsed.get('NODE_IP') != config.parsed.get( 'NETWORK_NODE_IP'): admin_token = util.get_api_token( 'http://%s:%d' % (config.parsed.get('NETWORK_NODE_IP'), config.parsed.get('API_PORT')), namespace='system') r = requests.request(flask.request.environ['REQUEST_METHOD'], 'http://%s:%d%s' % (config.parsed.get('NETWORK_NODE_IP'), config.parsed.get('API_PORT'), flask.request.environ['PATH_INFO']), data=flask.request.data, headers={ 'Authorization': admin_token, 'User-Agent': util.get_user_agent() }) logutil.info( None, 'Returning proxied request: %d, %s' % (r.status_code, r.text)) resp = flask.Response(r.text, mimetype='application/json') resp.status_code = r.status_code return resp return func(*args, **kwargs)
def wrapper(*args, **kwargs): i = kwargs.get('instance_from_db_virt') if i and i.db_entry['node'] != config.parsed.get('NODE_NAME'): url = 'http://%s:%d%s' % (i.db_entry['node'], config.parsed.get('API_PORT'), flask.request.environ['PATH_INFO']) api_token = util.get_api_token( 'http://%s:%d' % (i.db_entry['node'], config.parsed.get('API_PORT')), namespace=get_jwt_identity()) r = requests.request(flask.request.environ['REQUEST_METHOD'], url, data=json.dumps(flask_get_post_body()), headers={ 'Authorization': api_token, 'User-Agent': util.get_user_agent() }) logutil.info( None, 'Proxied %s %s returns: %d, %s' % (flask.request.environ['REQUEST_METHOD'], url, r.status_code, r.text)) resp = flask.Response(r.text, mimetype='application/json') resp.status_code = r.status_code return resp return func(*args, **kwargs)
def post(self, netblock=None, provide_dhcp=None, provide_nat=None, name=None, namespace=None): try: ipaddress.ip_network(netblock) except ValueError as e: return error(400, 'cannot parse netblock: %s' % e) if not namespace: namespace = get_jwt_identity() # If accessing a foreign name namespace, we need to be an admin if get_jwt_identity() not in [namespace, 'system']: return error( 401, 'only admins can create resources in a different namespace') network = db.allocate_network(netblock, provide_dhcp, provide_nat, name, namespace) db.add_event('network', network['uuid'], 'api', 'create', None, None) # Networks should immediately appear on the network node with db.get_lock('sf/network/%s' % network['uuid'], ttl=900) as _: if config.parsed.get('NODE_IP') == config.parsed.get( 'NETWORK_NODE_IP'): n = net.from_db(network['uuid']) if not n: LOG.info( 'network(%s): network not found, genuinely missing' % network['uuid']) return error(404, 'network not found') n.create() n.ensure_mesh() else: admin_token = util.get_api_token( 'http://%s:%d' % (config.parsed.get('NETWORK_NODE_IP'), config.parsed.get('API_PORT')), namespace=namespace) requests.request('put', ('http://%s:%d/deploy_network_node' % (config.parsed.get('NETWORK_NODE_IP'), config.parsed.get('API_PORT'))), data=json.dumps({'uuid': network['uuid']}), headers={ 'Authorization': admin_token, 'User-Agent': util.get_user_agent() }) db.add_event('network', network['uuid'], 'api', 'created', None, None) db.update_network_state(network['uuid'], 'created') # Initialise metadata db.persist_metadata('network', network['uuid'], {}) return network
def remove_dhcp(self): if config.parsed.get('NODE_IP') == config.parsed.get('NETWORK_NODE_IP'): subst = self.subst_dict() with util.RecordedOperation('remove dhcp', self) as _: with db.get_lock('sf/net/%s' % self.uuid, ttl=120) as _: d = dhcp.DHCP(self.uuid, subst['vx_veth_inner']) d.remove_dhcpd() else: admin_token = util.get_api_token( 'http://%s:%d' % (config.parsed.get('NETWORK_NODE_IP'), config.parsed.get('API_PORT')), namespace='system') requests.request( 'put', ('http://%s:%d/remove_dhcp' % (config.parsed.get('NETWORK_NODE_IP'), config.parsed.get('API_PORT'))), data=json.dumps({'uuid': self.uuid}), headers={'Authorization': admin_token, 'User-Agent': util.get_user_agent()})
def create(self): subst = self.subst_dict() with db.get_lock('sf/net/%s' % self.uuid, ttl=120) as _: if not util.check_for_interface(subst['vx_interface']): with util.RecordedOperation('create vxlan interface', self) as _: processutils.execute( 'ip link add %(vx_interface)s type vxlan id %(vx_id)s ' 'dev %(physical_interface)s dstport 0' % subst, shell=True) processutils.execute( 'sysctl -w net.ipv4.conf.%(vx_interface)s.arp_notify=1' % subst, shell=True) if not util.check_for_interface(subst['vx_bridge']): with util.RecordedOperation('create vxlan bridge', self) as _: processutils.execute( 'ip link add %(vx_bridge)s type bridge' % subst, shell=True) processutils.execute( 'ip link set %(vx_interface)s master %(vx_bridge)s' % subst, shell=True) processutils.execute( 'ip link set %(vx_interface)s up' % subst, shell=True) processutils.execute( 'ip link set %(vx_bridge)s up' % subst, shell=True) processutils.execute( 'sysctl -w net.ipv4.conf.%(vx_bridge)s.arp_notify=1' % subst, shell=True) processutils.execute( 'brctl setfd %(vx_bridge)s 0' % subst, shell=True) processutils.execute( 'brctl stp %(vx_bridge)s off' % subst, shell=True) processutils.execute( 'brctl setageing %(vx_bridge)s 0' % subst, shell=True) if config.parsed.get('NODE_IP') == config.parsed.get('NETWORK_NODE_IP'): if not os.path.exists('/var/run/netns/%(netns)s' % subst): with util.RecordedOperation('create netns', self) as _: processutils.execute( 'ip netns add %(netns)s' % subst, shell=True) if not util.check_for_interface(subst['vx_veth_outer']): with util.RecordedOperation('create router veth', self) as _: processutils.execute( 'ip link add %(vx_veth_outer)s type veth peer name %(vx_veth_inner)s' % subst, shell=True) processutils.execute( 'ip link set %(vx_veth_inner)s netns %(netns)s' % subst, shell=True) processutils.execute( 'brctl addif %(vx_bridge)s %(vx_veth_outer)s' % subst, shell=True) processutils.execute( 'ip link set %(vx_veth_outer)s up' % subst, shell=True) processutils.execute( '%(in_netns)s ip link set %(vx_veth_inner)s up' % subst, shell=True) processutils.execute( '%(in_netns)s ip addr add %(router)s/%(netmask)s dev %(vx_veth_inner)s' % subst, shell=True) if not util.check_for_interface(subst['physical_veth_outer']): with util.RecordedOperation('create physical veth', self) as _: processutils.execute( 'ip link add %(physical_veth_outer)s type veth peer name ' '%(physical_veth_inner)s' % subst, shell=True) processutils.execute( 'brctl addif %(physical_bridge)s %(physical_veth_outer)s' % subst, shell=True) processutils.execute( 'ip link set %(physical_veth_outer)s up' % subst, shell=True) processutils.execute( 'ip link set %(physical_veth_inner)s netns %(netns)s' % subst, shell=True) self.deploy_nat() self.update_dhcp() else: admin_token = util.get_api_token( 'http://%s:%d' % (config.parsed.get('NETWORK_NODE_IP'), config.parsed.get('API_PORT')), namespace='system') requests.request( 'put', ('http://%s:%d/deploy_network_node' % (config.parsed.get('NETWORK_NODE_IP'), config.parsed.get('API_PORT'))), data=json.dumps({'uuid': self.uuid}), headers={'Authorization': admin_token, 'User-Agent': util.get_user_agent()})
def post(self, name=None, cpus=None, memory=None, network=None, disk=None, ssh_key=None, user_data=None, placed_on=None, namespace=None, instance_uuid=None): global SCHEDULER # We need to sanitise the name so its safe for DNS name = re.sub(r'([^a-zA-Z0-9_\-])', '', name) if not namespace: namespace = get_jwt_identity() # If accessing a foreign namespace, we need to be an admin if get_jwt_identity() not in [namespace, 'system']: return error( 401, 'only admins can create resources in a different namespace') # The instance needs to exist in the DB before network interfaces are created if not instance_uuid: instance_uuid = str(uuid.uuid4()) db.add_event('instance', instance_uuid, 'uuid allocated', None, None, None) # Create instance object instance = virt.from_db(instance_uuid) if instance: if get_jwt_identity() not in [ instance.db_entry['namespace'], 'system' ]: LOG.info('instance(%s): instance not found, ownership test' % instance_uuid) return error(404, 'instance not found') if not instance: instance = virt.from_definition(uuid=instance_uuid, name=name, disks=disk, memory_mb=memory, vcpus=cpus, ssh_key=ssh_key, user_data=user_data, owner=namespace) if not SCHEDULER: SCHEDULER = scheduler.Scheduler() # Have we been placed? if not placed_on: candidates = SCHEDULER.place_instance(instance, network) if len(candidates) == 0: db.add_event('instance', instance_uuid, 'schedule', 'failed', None, 'insufficient resources') db.update_instance_state(instance_uuid, 'error') return error(507, 'insufficient capacity') placed_on = candidates[0] db.place_instance(instance_uuid, placed_on) db.add_event('instance', instance_uuid, 'placement', None, None, placed_on) else: try: candidates = SCHEDULER.place_instance(instance, network, candidates=[placed_on]) if len(candidates) == 0: db.add_event('instance', instance_uuid, 'schedule', 'failed', None, 'insufficient resources') db.update_instance_state(instance_uuid, 'error') return error(507, 'insufficient capacity') except scheduler.CandidateNodeNotFoundException as e: return error(404, 'node not found: %s' % e) # Have we been placed on a different node? if not placed_on == config.parsed.get('NODE_NAME'): body = flask_get_post_body() body['placed_on'] = placed_on body['instance_uuid'] = instance_uuid body['namespace'] = namespace token = util.get_api_token( 'http://%s:%d' % (placed_on, config.parsed.get('API_PORT')), namespace=namespace) r = requests.request('POST', 'http://%s:%d/instances' % (placed_on, config.parsed.get('API_PORT')), data=json.dumps(body), headers={ 'Authorization': token, 'User-Agent': util.get_user_agent() }) LOG.info('Returning proxied request: %d, %s' % (r.status_code, r.text)) resp = flask.Response(r.text, mimetype='application/json') resp.status_code = r.status_code return resp # Check we can get the required IPs nets = {} allocations = {} def error_with_cleanup(status_code, message): for network_uuid in allocations: n = net.from_db(network_uuid) for addr, _ in allocations[network_uuid]: with db.get_lock('sf/ipmanager/%s' % n.uuid, ttl=120) as _: ipm = db.get_ipmanager(n.uuid) ipm.release(addr) db.persist_ipmanager(n.uuid, ipm.save()) return error(status_code, message) order = 0 if network: for netdesc in network: if 'network_uuid' not in netdesc or not netdesc['network_uuid']: return error_with_cleanup(404, 'network not specified') if netdesc['network_uuid'] not in nets: n = net.from_db(netdesc['network_uuid']) if not n: return error_with_cleanup( 404, 'network %s not found' % netdesc['network_uuid']) nets[netdesc['network_uuid']] = n n.create() with db.get_lock('sf/ipmanager/%s' % netdesc['network_uuid'], ttl=120) as _: db.add_event('network', netdesc['network_uuid'], 'allocate address', None, None, instance_uuid) allocations.setdefault(netdesc['network_uuid'], []) ipm = db.get_ipmanager(netdesc['network_uuid']) if 'address' not in netdesc or not netdesc['address']: netdesc['address'] = ipm.get_random_free_address() else: if not ipm.reserve(netdesc['address']): return error_with_cleanup( 409, 'address %s in use' % netdesc['address']) db.persist_ipmanager(netdesc['network_uuid'], ipm.save()) allocations[netdesc['network_uuid']].append( (netdesc['address'], order)) if 'model' not in netdesc or not netdesc['model']: netdesc['model'] = 'virtio' db.create_network_interface(str(uuid.uuid4()), netdesc, instance_uuid, order) order += 1 # Initialise metadata db.persist_metadata('instance', instance_uuid, {}) # Now we can start the instance with db.get_lock('sf/instance/%s' % instance.db_entry['uuid'], ttl=900) as lock: with util.RecordedOperation('ensure networks exist', instance) as _: for network_uuid in nets: n = nets[network_uuid] n.ensure_mesh() n.update_dhcp() with util.RecordedOperation('instance creation', instance) as _: instance.create(lock=lock) for iface in db.get_instance_interfaces(instance.db_entry['uuid']): db.update_network_interface_state(iface['uuid'], 'created') return db.get_instance(instance_uuid)