diff --git a/000-default-conf b/000-default-conf
deleted file mode 100644
index b102f1b..0000000
--- a/000-default-conf
+++ /dev/null
@@ -1,72 +0,0 @@
-
- # The ServerName directive sets the request scheme, hostname and port that
- # the server uses to identify itself. This is used when creating
- # redirection URLs. In the context of virtual hosts, the ServerName
- # specifies what hostname must appear in the request's Host: header to
- # match this virtual host. For the default virtual host (this file) this
- # value is not decisive as it is used as a last resort host regardless.
- # However, you must set it for any further virtual host explicitly.
- #ServerName www.example.com
-
- ServerAdmin webmaster@localhost
- DocumentRoot /var/www/html
-
- # Available loglevels: trace8, ..., trace1, debug, info, notice, warn,
- # error, crit, alert, emerg.
- # It is also possible to configure the loglevel for particular
- # modules, e.g.
- #LogLevel info ssl:warn
-
- ErrorLog ${APACHE_LOG_DIR}/error.log
- CustomLog ${APACHE_LOG_DIR}/access.log combined
-
- # For most configuration files from conf-available/, which are
- # enabled or disabled at a global level, it is possible to
- # include a line for only one particular virtual host. For example the
- # following line enables the CGI configuration for this host only
- # after it has been globally disabled with "a2disconf".
- #Include conf-available/serve-cgi-bin.conf
-
-
-# Run the Django app as the clearinghouse user
-WSGIDaemonProcess chdjango user=ch processes=5 threads=10
-WSGIProcessGroup chdjango
-
-# HTTP
-
- # Redirect requests for the server index page or that are
- # clearinghouse-related to the HTTPS site.
- RedirectMatch ^/$ https://10.0.2.15/ch/html/login
- RedirectMatch ^/ch https://10.0.2.15/ch/html/login
-
-
-# SSL
-
- ServerAdmin webmaster@localhost
-
- # Enable SSL
- SSLEngine on
- SSLCertificateFile /etc/apache2/ssl/server.crt
- SSLCertificateKeyFile /etc/apache2/ssl/server.key
- # You can add intermediate certificates here.
-
- # Point Apache to the clearinghouse's static images/CSS/JavaScript
- Alias /site_media /home/ch/deployment/clearinghouse/website/html/media
-
- Require all granted
-
-
- # XXX We should configure the Django admin page static files too!
- # XXX See https://docs.djangoproject.com/en/1.6/howto/deployment/wsgi/modwsgi/
-
- # Point the URL https://mysite/ch to the Django app
- WSGIScriptAlias /ch /home/ch/deployment/clearinghouse/wsgi/wsgi.py
-
-
-
- Require all granted
-
-
-
-
-# vim: syntax=apache ts=4 sw=4 sts=4 sr noet
diff --git a/apache.conf b/apache.conf
deleted file mode 100644
index a741592..0000000
--- a/apache.conf
+++ /dev/null
@@ -1,41 +0,0 @@
-# Run the Django app as the clearinghouse user
-WSGIDaemonProcess chdjango user=abhishek processes=5 threads=10
-WSGIProcessGroup chdjango
-
-# HTTP
-
- # Redirect requests for the server index page or that are
- # clearinghouse-related to the HTTPS site.
- RedirectMatch ^/$ https://abhi/abhishek/html/login
- RedirectMatch ^/ch https://abhi/abhishek/html/login
-
-
-# SSL
-
- ServerAdmin webmaster@localhost
-
- # Enable SSL
- SSLEngine on
- SSLCertificateFile /etc/apache2/ssl/server.crt
- SSLCertificateKeyFile /etc/apache2/ssl/server.key
- # You can add intermediate certificates here.
-
- # Point Apache to the clearinghouse's static images/CSS/JavaScript
- Alias /site_media /home/abhishek/deployment/clearinghouse/website/html/media
-
- Require all granted
-
-
- # XXX We should configure the Django admin page static files too!
- # XXX See https://docs.djangoproject.com/en/1.6/howto/deployment/wsgi/modwsgi/
-
- # Point the URL https://abhi/abhishek to the Django app
- WSGIScriptAlias /abhishek /home/abhishek/deployment/clearinghouse/wsgi/wsgi.py
-
-
-
- Require all granted
-
-
-
-
diff --git a/client_connect.r2py b/client_connect.r2py
deleted file mode 100644
index 07bce5d..0000000
--- a/client_connect.r2py
+++ /dev/null
@@ -1,5 +0,0 @@
-if callfunc == 'initialize':
- myip = getmyip()
- myport = 12346
- client_obj = openconnection('10.0.0.2',12345,myip,myport,10)
- log(client_obj)
diff --git a/emulcomm.py b/emulcomm.py
deleted file mode 100644
index cf4a664..0000000
--- a/emulcomm.py
+++ /dev/null
@@ -1,2246 +0,0 @@
-"""
- Author: Justin Cappos, Armon Dadgar
-
- Start Date: 27 June 2008
-
- Description:
-
- This is a collection of communications routines that provide a programmer
- with a reasonable environment. This is used by repy.py to provide a
- highly restricted (but usable) environment.
-"""
-
-import socket
-
-# Armon: Used to check if a socket is ready
-import select
-
-# socket uses getattr and setattr. We need to make these available to it...
-socket.getattr = getattr
-socket.setattr = setattr
-
-
-# needed to set threads for recvmess and waitforconn
-import threading
-# threading in python2.7 uses hasattr. It needs to be made available.
-threading.hasattr = hasattr
-
-
-# So I can exit all threads when an error occurs or do select
-import harshexit
-
-# Needed for finding out info about sockets, available interfaces, etc
-import nonportable
-
-# So I can print a clean traceback when an error happens
-import tracebackrepy
-
-# accounting
-# id(sock) will be used to register and unregister sockets with nanny
-import nanny
-
-# give me uniqueIDs for the comminfo table
-import idhelper
-
-# for sleep
-import time
-
-# Armon: Used for decoding the error messages
-import errno
-
-# Armon: Used for getting the constant IP values for resolving our external IP
-import repy_constants
-
-# Get the exceptions
-from exception_hierarchy import *
-
-###### Module Data
-
-# This is a library of all currently bound sockets. Since multiple
-# UDP bindings on a single port is hairy, we store bound sockets
-# here, and use them for both sending and receiving if they are
-# available. This feels slightly byzantine, but it allows us to
-# avoid modifying the repy API.
-#
-# Format of entries is as follows:
-# Key - 3-tuple of ("UDP", IP, Port)
-# Val - Bound socket object
-_BOUND_SOCKETS = {} # Ticket = 1015 (Resolved)
-
-# If we have a preference for an IP/Interface this flag is set to True
-user_ip_interface_preferences = False
-
-# Do we allow non-specified IPs
-allow_nonspecified_ips = True
-
-# Armon: Specified the list of allowed IP and Interfaces in order of their preference
-# The basic structure is list of tuples (IP, Value), IP is True if its an IP, False if its an interface
-user_specified_ip_interface_list = []
-
-# This list caches the allowed IP's
-# It is updated at the launch of repy, or by calls to getmyip and update_ip_cache
-# NOTE: The loopback address 127.0.0.1 is always permitted. update_ip_cache will always add this
-# if it is not specified explicitly by the user
-allowediplist = []
-cachelock = threading.Lock() # This allows only a single simultaneous cache update
-
-
-##### Internal Functions
-
-# Determines if a specified IP address is allowed in the context of user settings
-def _ip_is_allowed(ip):
- """
-
- Determines if a given IP is allowed, by checking against the cached allowed IP's.
-
-
- ip: The IP address to search for.
-
-
- True, if allowed. False, otherwise.
- """
- global allowediplist
- global user_ip_interface_preferences
- global allow_nonspecified_ips
-
- # If there is no preference, anything goes
- # same with allow_nonspecified_ips
- if not user_ip_interface_preferences or allow_nonspecified_ips:
- return True
-
- # Check the list of allowed IP's
- return (ip in allowediplist)
-
-
-# Only appends the elem to lst if the elem is unique
-def _unique_append(lst, elem):
- if elem not in lst:
- lst.append(elem)
-
-# This function updates the allowed IP cache
-# It iterates through all possible IP's and stores ones which are bindable as part of the allowediplist
-def update_ip_cache():
- global allowediplist
- global user_ip_interface_preferences
- global user_specified_ip_interface_list
- global allow_nonspecified_ips
-
- # If there is no preference, this is a no-op
- if not user_ip_interface_preferences:
- return
-
- # Acquire the lock to update the cache
- cachelock.acquire()
-
- # If there is any exception release the cachelock
- try:
- # Stores the IP's
- allowed_list = []
-
- # Iterate through the allowed list, handle each element
- for (is_ip_addr, value) in user_specified_ip_interface_list:
- # Handle normal IP's
- if is_ip_addr:
- _unique_append(allowed_list, value)
-
- # Handle interfaces
- else:
- try:
- # Get the IP's associated with the NIC
- interface_ips = nonportable.os_api.get_interface_ip_addresses(value)
- for interface_ip in interface_ips:
- _unique_append(allowed_list, interface_ip)
- except:
- # Catch exceptions if the NIC does not exist
- pass
-
- # This will store all the IP's that we are able to bind to
- bindable_list = []
-
- # Try binding to every ip
- for ip in allowed_list:
- sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
- try:
- sock.bind((ip,0))
- except:
- pass # Not a good ip, skip it
- else:
- bindable_list.append(ip) # This is a good ip, store it
- finally:
- sock.close()
-
- # Add loopback
- _unique_append(bindable_list, "127.0.0.1")
-
- # Update the global cache
- allowediplist = bindable_list
-
- finally:
- # Release the lock
- cachelock.release()
-
-
-############## General Purpose socket functions ##############
-
-def _is_already_connected_exception(exceptionobj):
- """
-
- Determines if a given error number indicates that the socket
- is already connected.
-
-
- An exception object from a network call.
-
-
- True if already connected, false otherwise
- """
- # Get the type
- exception_type = type(exceptionobj)
-
- # Only continue if the type is socket.error
- if exception_type is not socket.error:
- return False
-
- # Get the error number
- errnum = exceptionobj[0]
-
- # Store a list of error messages meaning we are connected
- connected_errors = ["EISCONN", "WSAEISCONN"]
-
- # Convert the errno to and error string name
- try:
- errname = errno.errorcode[errnum]
- except Exception,e:
- # The error is unknown for some reason...
- errname = None
-
- # Return if the error name is in our white list
- return (errname in connected_errors)
-
-
-def _is_addr_in_use_exception(exceptionobj):
- """
-
- Determines if a given error number indicates that the provided
- localip / localport are already bound and that the unique
- tuple is already in use.
-
-
- An exception object from a network call.
-
-
- True if already in use, false otherwise
- """
- # Get the type
- exception_type = type(exceptionobj)
-
- # Only continue if the type is socket.error
- if exception_type is not socket.error:
- return False
-
- # Get the error number
- errnum = exceptionobj[0]
-
- # Store a list of error messages meaning we are in use
- in_use_errors = ["EADDRINUSE", "WSAEADDRINUSE"]
-
- # Convert the errno to and error string name
- try:
- errname = errno.errorcode[errnum]
- except Exception,e:
- # The error is unknown for some reason...
- errname = None
-
- # Return if the error name is in our white list
- return (errname in in_use_errors)
-
-
-def _is_addr_unavailable_exception(exceptionobj):
- """
-
- Determines if a given error number indicates that the provided
- localip is not available during a bind() call.
- This indicates an AddressBindingError should be raised.
-
-
- An exception object from a network call.
-
-
- True if already in use, false otherwise
- """
- # Get the type
- exception_type = type(exceptionobj)
-
- # Only continue if the type is socket.error
- if exception_type is not socket.error:
- return False
-
- # Get the error number
- errnum = exceptionobj[0]
-
- # Store a list of error messages meaning the address is not available
- not_avail_errors = ["EADDRNOTAVAIL", "WSAEADDRNOTAVAIL"]
-
- # Convert the errno to and error string name
- try:
- errname = errno.errorcode[errnum]
- except Exception,e:
- # The error is unknown for some reason...
- errname = None
-
- # Return if the error name is in our white list
- return (errname in not_avail_errors)
-
-
-def _is_conn_refused_exception(exceptionobj):
- """
-
- Determines if a given error number indicates that the remote
- host has actively refused the connection. E.g.
- ECONNREFUSED
-
-
- An exception object from a network call.
-
-
- True if the error indicates the connection was refused, false otherwise
- """
- # Get the type
- exception_type = type(exceptionobj)
-
- # Only continue if the type is socket.error
- if exception_type is not socket.error:
- return False
-
- # Get the error number
- errnum = exceptionobj[0]
-
- # Store a list of error messages meaning the host refused
- refused_errors = ["ECONNREFUSED", "WSAECONNREFUSED"]
-
- # Convert the errno to and error string name
- try:
- errname = errno.errorcode[errnum]
- except Exception,e:
- # The error is unknown for some reason...
- errname = None
-
- # Return if the error name is in our white list
- return (errname in refused_errors)
-
-
-def _is_network_down_exception(exceptionobj):
- """
-
- Determines if a given error number indicates that the
- network is down.
-
-
- An exception object from a network call.
-
-
- True if the network is down, false otherwise
- """
- # Get the type
- exception_type = type(exceptionobj)
-
- # Only continue if the type is socket.error
- if exception_type is not socket.error:
- return False
-
- # Get the error number
- errnum = exceptionobj[0]
-
- # These error messages mean we are disconnected.
- # (The "WSA" ones are the Windows Sockets API's renditions of
- # the Linux/BSD errno.h preprocessor macros).
- net_down_errors = ["ENETDOWN", "EHOSTUNREACH", "ENETUNREACH",
- "WSAENETDOWN", "WSAEHOSTUNREACH", "WSAENETUNREACH"]
-
- # Convert the errno to and error string name
- try:
- errname = errno.errorcode[errnum]
- except Exception,e:
- # The error is unknown for some reason...
- errname = None
-
- # Return if the error name is in our white list
- return (errname in net_down_errors)
-
-
-def _is_recoverable_network_exception(exceptionobj):
- """
-
- Determines if a given error number is recoverable or fatal.
-
-
- An exception object from a network call.
-
-
- True if potentially recoverable, False if fatal.
- """
- # Get the type
- exception_type = type(exceptionobj)
-
- # socket.timeout is recoverable always
- if exception_type == socket.timeout:
- return True
-
- # Only continue if the type is socket.error or select.error
- elif exception_type != socket.error and exception_type != select.error:
- return False
-
- # Get the error number
- errnum = exceptionobj[0]
-
- # Store a list of recoverable error numbers
- recoverable_errors = ["EINTR","EAGAIN","EBUSY","EWOULDBLOCK","ETIMEDOUT","ERESTART",
- "WSAEINTR","WSAEWOULDBLOCK","WSAETIMEDOUT","EALREADY","WSAEALREADY",
- "EINPROGRESS","WSAEINPROGRESS"]
-
- # Convert the errno to and error string name
- try:
- errname = errno.errorcode[errnum]
- except Exception,e:
- # The error is unknown for some reason...
- errname = None
-
- # Return if the error name is in our white list
- return (errname in recoverable_errors)
-
-
-# Determines based on exception if the connection has been terminated
-def _is_terminated_connection_exception(exceptionobj):
- """
-
- Determines if the exception is indicated the connection is terminated.
-
-
- An exception object from a network call.
-
-
- True if the connection is terminated, False otherwise.
- False means we could not determine with certainty if the socket is closed.
- """
- # Get the type
- exception_type = type(exceptionobj)
-
- # We only want to continue if it is socket.error or select.error
- if exception_type != socket.error and exception_type != select.error:
- return False
-
- # Get the error number
- errnum = exceptionobj[0]
-
- # Store a list of errors which indicate connection closed
- connection_closed_errors = ["EPIPE","EBADF","EBADR","ENOLINK","EBADFD","ENETRESET",
- "ECONNRESET","WSAEBADF","WSAENOTSOCK","WSAECONNRESET",]
-
- # Convert the errnum to an error string
- try:
- errname = errno.errorcode[errnum]
- except:
- # The error number is not defined...
- errname = None
-
- # Return whether the errname is in our pre-defined list
- return (errname in connection_closed_errors)
-
-
-
-# Armon: This is used for semantics, to determine if we have a valid IP.
-def _is_valid_ip_address(ipaddr):
- """
-
- Determines if ipaddr is a valid IP address.
- 0.X and 224-255.X addresses are not allowed.
- Additionally, 192.168.0.0 is not allowed.
-
-
- ipaddr: String to check for validity. (It will check that this is a string).
-
-
- True if a valid IP, False otherwise.
- """
- # Argument must be of the string type
- if not type(ipaddr) == str:
- return False
-
- if ipaddr == '192.168.0.0':
- return False
-
- # A valid IP should have 4 segments, explode on the period
- octets = ipaddr.split(".")
-
- # Check that we have 4 parts
- if len(octets) != 4:
- return False
-
- # Check that each segment is a number between 0 and 255 inclusively.
- for octet in octets:
- # Attempt to convert to an integer
- try:
- ipnumber = int(octet)
- except ValueError:
- # There was an error converting to an integer, not an IP
- return False
-
- # IP addresses octets must be between 0 and 255
- if not (ipnumber >= 0 and ipnumber <= 255):
- return False
-
- # should not have a ValueError (I already checked)
- firstipnumber = int(octets[0])
-
- # IP addresses with the first octet 0 refer to all local IPs. These are
- # not allowed
- if firstipnumber == 0:
- return False
-
- # IP addresses with the first octet >=224 are either Multicast or reserved.
- # These are not allowed
- if firstipnumber >= 224:
- return False
-
- # At this point, assume the IP is valid
- return True
-
-
-# Armon: This is used for semantics, to determine if the given port is valid
-def _is_valid_network_port(port):
- """
-
- Determines if a given network port is valid.
-
-
- port: A numeric type (this will be checked) port number.
-
-
- True if valid, False otherwise.
- """
- # Check the type is int or long
- if not (type(port) == long or type(port) == int):
- return False
-
- if port >= 1 and port <= 65535:
- return True
- else:
- return False
-
-
-# Used to decide if an IP is the loopback IP or not. This is needed for
-# accounting
-def _is_loopback_ipaddr(host):
- if not host.startswith('127.'):
- return False
- if len(host.split('.')) != 4:
- return False
-
- octets = host.split('.')
- if len(octets) != 4:
- return False
- for octet in octets:
- try:
- if int(octet) > 255 or int(octet) < 0:
- return False
- except ValueError:
- return False
-
- return True
-
-
-# Checks if binding to the local port is allowed
-# type should be "TCP" or "UDP".
-def _is_allowed_localport(type, localport):
- # Switch to the proper resource
- if type == "TCP":
- resource = "connport"
- elif type == "UDP":
- resource = "messport"
- else:
- raise InternalRepyError("Bad type specified for _is_allowed_localport()")
-
- # Check what is allowed by nanny
- return nanny.is_item_allowed(resource, float(localport))
-
-
-
-
-######################### Simple Public Functions ##########################
-
-
-
-# Public interface
-def gethostbyname(name):
- """
-
- Provides information about a hostname. Calls socket.gethostbyname().
- Translate a host name to IPv4 address format. The IPv4 address is
- returned as a string, such as '100.50.200.5'. If the host name is an
- IPv4 address itself it is returned unchanged.
-
-
- name:
- The host name to translate.
-
-
- RepyArgumentError (descends from NetworkError) if the name is not a string
- NetworkAddressError (descends from NetworkError) if the address cannot
- be resolved.
-
-
- None.
-
-
- This operation consumes network bandwidth of 4K netrecv, 1K netsend.
- (It's hard to tell how much was actually sent / received at this level.)
-
-
- The IPv4 address as a string.
- """
-
- if type(name) is not str:
- raise RepyArgumentError("gethostbyname() takes a string as argument.")
-
- # charge 4K for a look up... I don't know the right number, but we should
- # charge something. We'll always charge to the netsend interface...
- nanny.tattle_quantity('netsend', 1024)
- nanny.tattle_quantity('netrecv', 4096)
-
- try:
- return socket.gethostbyname(name)
- except socket.gaierror:
- raise NetworkAddressError("The hostname '"+name+"' could not be resolved.")
-
-
-
-# Public interface
-def getmyip():
- """
-
- Provides the IP of this computer on its public facing interface.
- Does some clever trickery.
-
-
- None
-
-
- InternetConnectivityError is the host is not connected to the internet.
-
-
- None.
-
-
- This operations consumes 256 netsend and 128 netrecv.
-
-
- The localhost's IP address
- """
- # Charge for the resources
- nanny.tattle_quantity("netsend", 256)
- nanny.tattle_quantity("netrecv", 128)
-
- # I got some of this from: http://groups.google.com/group/comp.lang.python/browse_thread/thread/d931cdc326d7032b?hl=en
-
- # Update the cache and return the first allowed IP
- # Only if a preference is set
- if user_ip_interface_preferences:
- update_ip_cache()
- # Return the first allowed ip, there is always at least 1 element (loopback)
- return allowediplist[0]
-
- # Initialize these to None, so we can detect a failure
- myip = None
-
- # It's possible on some platforms (Windows Mobile) that the IP will be
- # 0.0.0.0 even when I have a public IP and the external IP is up. However, if
- # I get a real connection with SOCK_STREAM, then I should get the real
- # answer.
-
- # Try each stable IP
- for ip_addr in repy_constants.STABLE_PUBLIC_IPS:
- try:
- # Try to resolve using the current connection type and
- # stable IP, using port 80 since some platforms panic
- # when given 0 (FreeBSD)
- myip = _get_localIP_to_remoteIP(socket.SOCK_DGRAM, ip_addr, 80)
- except (socket.error, socket.timeout):
- # We can ignore any networking related errors, since we want to try
- # the other connection types and IP addresses. If we fail,
- # we will eventually raise an exception anyways.
- pass
- else:
- # Return immediately if the IP address is good
- if _is_valid_ip_address(myip):
- return myip
-
-
- # Since we haven't returned yet, we must have failed.
- # Raise an exception, we must not be connected to the internet
- raise InternetConnectivityError("Cannot detect a connection to the Internet.")
-
-
-
-def _get_localIP_to_remoteIP(connection_type, external_ip, external_port=80):
- """
-
- Resolve the local ip used when connecting outbound to an external ip.
-
-
- connection_type:
- The type of connection to attempt. See socket.socket().
-
- external_ip:
- The external IP to attempt to connect to.
-
- external_port:
- The port on the remote host to attempt to connect to.
-
-
- As with socket.socket(), socketobj.connect(), etc.
-
-
- The locally assigned IP for the connection.
- """
- # Open a socket
- sockobj = socket.socket(socket.AF_INET, connection_type)
-
- # Make sure that the socket obj doesn't hang forever in
- # case connect() is blocking. Fix to #1003
- sockobj.settimeout(1.0)
-
- try:
- sockobj.connect((external_ip, external_port))
-
- # Get the local connection information for this socket
- (myip, localport) = sockobj.getsockname()
-
- # Always close the socket
- finally:
- sockobj.close()
-
- return myip
-
-
-
-
-###################### Shared message / connection items ###################
-
-
-
-
-# Armon: How frequently should we check for the availability of the socket?
-RETRY_INTERVAL = 0.2 # In seconds
-
-
-def _cleanup_socket(self):
- """
-
- Internal cleanup method for open sockets. The socket
- lock for the socket should be acquired prior to
- calling.
-
-
- None
-
- The insocket/outsocket handle will be released.
-
-
- InternalRepyError is raised if the socket lock is not held
- prior to calling the function.
-
-
- None
- """
- sock = self.socketobj
- socket_lock = self.sock_lock
- # Make sure the lock is already acquired
- # BUG: We don't know which thread exactly acquired the lock.
- if socket_lock.acquire(False):
- socket_lock.release()
- raise InternalRepyError("Socket lock should be acquired before calling _cleanup_socket!")
-
- if (sock == None):
- # Already cleaned up
- return
- # Shutdown the socket for writing prior to close
- # to unblock any threads that are writing
- try:
- sock.shutdown(socket.SHUT_WR)
- except:
- pass
-
- # Close the socket
- try:
- sock.close()
- except:
- pass
- # socket id is used to unregister socket with nanny
- sockid = id(sock)
- # Re-store resources
- nanny.tattle_remove_item('insockets', sockid)
- nanny.tattle_remove_item('outsockets', sockid)
-
-
-####################### Message sending #############################
-
-
-
-# Public interface!!!
-def sendmessage(destip, destport, message, localip, localport):
- """
-
- Send a message to a host / port
-
-
- destip:
- The host to send a message to
- destport:
- The port to send the message to
- message:
- The message to send
- localhost:
- The local IP to send the message from
- localport:
- The local port to send the message from
-
-
- AddressBindingError (descends NetworkError) when the local IP isn't
- a local IP.
-
- ResourceForbiddenError (descends ResourceException?) when the local
- port isn't allowed
-
- RepyArgumentError when the local IP and port aren't valid types
- or values
-
- AlreadyListeningError if there is an existing listening UDP socket
- on the same local IP and port.
-
- DuplicateTupleError if there is another sendmessage on the same
- local IP and port to the same remote host.
-
-
- None.
-
-
- This operation consumes 64 bytes + number of bytes of the message that
- were transmitted. This requires that the localport is allowed.
-
-
- The number of bytes sent on success
- """
- # Check the input arguments (type)
- if type(destip) is not str:
- raise RepyArgumentError("Provided destip must be a string!")
- if type(localip) is not str:
- raise RepyArgumentError("Provided localip must be a string!")
-
- if type(destport) is not int:
- raise RepyArgumentError("Provided destport must be an int!")
- if type(localport) is not int:
- raise RepyArgumentError("Provided localport must be an int!")
-
- if type(message) is not str:
- raise RepyArgumentError("Provided message must be a string!")
-
-
- # Check the input arguments (sanity)
- if not _is_valid_ip_address(destip):
- raise RepyArgumentError("Provided destip is not valid! IP: '"+destip+"'")
- if not _is_valid_ip_address(localip):
- raise RepyArgumentError("Provided localip is not valid! IP: '"+localip+"'")
-
- if not _is_valid_network_port(destport):
- raise RepyArgumentError("Provided destport is not valid! Port: "+str(destport))
- if not _is_valid_network_port(localport):
- raise RepyArgumentError("Provided localport is not valid! Port: "+str(localport))
-
-
- # Check that if localip == destip, then localport != destport
- if localip == destip and localport == destport:
- raise RepyArgumentError("Local socket name cannot match destination socket name! Local/Dest IP and Port match.")
-
- # Check the input arguments (permission)
- update_ip_cache()
- if not _ip_is_allowed(localip):
- raise ResourceForbiddenError("Provided localip is not allowed! IP: "+localip)
-
- if not _is_allowed_localport("UDP", localport):
- raise ResourceForbiddenError("Provided localport is not allowed! Port: "+str(localport))
-
- # Wait for netsend
- if _is_loopback_ipaddr(destip):
- nanny.tattle_quantity('loopsend', 0)
- else:
- nanny.tattle_quantity('netsend', 0)
-
- try:
- sock = None
-
- if ("UDP", localip, localport) in _BOUND_SOCKETS:
- sock = _BOUND_SOCKETS[("UDP", localip, localport)]
- else:
- # Get the socket
- sock = _get_udp_socket(localip, localport)
- # Register this socket with nanny
- nanny.tattle_add_item("outsockets", id(sock))
- # Send the message
- bytessent = sock.sendto(message, (destip, destport))
-
- # Account for the resources
- if _is_loopback_ipaddr(destip):
- nanny.tattle_quantity('loopsend', bytessent + 64)
- else:
- nanny.tattle_quantity('netsend', bytessent + 64)
-
- return bytessent
-
- except Exception, e:
-
- try:
- # If we're borrowing the socket, closing is not appropriate.
- if not ("UDP", localip, localport) in _BOUND_SOCKETS:
- sock.close()
- except:
- pass
-
- # Check if address is already in use
- if _is_addr_in_use_exception(e):
- raise DuplicateTupleError("Provided Local IP and Local Port is already in use!")
-
- if _is_addr_unavailable_exception(e):
- raise AddressBindingError("Cannot bind to the specified local ip, invalid!")
-
- # Unknown error...
- else:
- raise
-
-
-
-
-# Public interface!!!
-def listenformessage(localip, localport):
- """
-
- Sets up a UDPServerSocket to receive incoming UDP messages.
-
-
- localip:
- The local IP to register the handler on.
- localport:
- The port to listen on.
-
-
- DuplicateTupleError (descends NetworkError) if the port cannot be
- listened on because some other process on the system is listening on
- it.
-
- AlreadyListeningError if there is already a UDPServerSocket with the same
- IP and port.
-
- RepyArgumentError if the port number or ip is wrong type or obviously
- invalid.
-
- AddressBindingError (descends NetworkError) if the IP address isn't a
- local IP.
-
- ResourceForbiddenError if the port is not allowed.
-
-
- Prevents other UDPServerSockets from using this port / IP
-
-
- This operation consumes an insocket and requires that the provided messport is allowed.
-
-
- The UDPServerSocket.
- """
- # Check the input arguments (type)
- if type(localip) is not str:
- raise RepyArgumentError("Provided localip must be a string!")
-
- if type(localport) is not int:
- raise RepyArgumentError("Provided localport must be a int!")
-
-
- # Check the input arguments (sanity)
- if not _is_valid_ip_address(localip):
- raise RepyArgumentError("Provided localip is not valid! IP: '"+localip+"'")
-
- if not _is_valid_network_port(localport):
- raise RepyArgumentError("Provided localport is not valid! Port: "+str(localport))
-
-
- # Check the input arguments (permission)
- update_ip_cache()
- if not _ip_is_allowed(localip):
- raise ResourceForbiddenError("Provided localip is not allowed! IP: '"+localip+"'")
-
- if not _is_allowed_localport("UDP", localport):
- raise ResourceForbiddenError("Provided localport is not allowed! Port: "+str(localport))
- # This identity tuple will be used to check for an existing connection with same identity
- identity = ("UDP", localip, localport, None, None)
-
- try:
- # Check if localip is on loopback
- on_loopback = _is_loopback_ipaddr(localip)
-
- # Get the socket
- sock = _get_udp_socket(localip,localport)
-
- # Register this socket as an insocket
- nanny.tattle_add_item('insockets',id(sock))
-
- # Add the socket to _BOUND_SOCKETS so that we can
- # preserve send functionality on this port.
- _BOUND_SOCKETS[("UDP", localip, localport)] = sock
-
- except Exception, e:
-
- # Check if this an already in use error
- if _is_addr_in_use_exception(e):
- # Call _conn_cleanup_check to determine if this is because
- # the socket is being cleaned up or if it is actively being used or
- # if there is an existing listening socket
- # This will always raise DuplicateTupleError or
- # CleanupInProgressError or AlreadyListeningError
- _conn_cleanup_check(identity)
-
- # Check if this is a binding error
- if _is_addr_unavailable_exception(e):
- raise AddressBindingError("Cannot bind to the specified local ip, invalid!")
-
- # Unknown error...
- else:
- raise
-
- # Create a UDPServerSocket
- server_sock = UDPServerSocket(sock, on_loopback)
-
- # Return the UDPServerSocket
- return server_sock
-
-
-
-####################### Connection oriented #############################
-def _conn_alreadyexists_check(identity):
- """
-
- This private function checks if a socket that
- got EADDRINUSE is because the socket is active,
- or not
-
-
- identity: A tuple to check for cleanup
-
-
- Raises DuplicateTupleError if the socket is actively being used.
-
- Raises AddressBindingError if the binding is not allowed
-
-
- None
- """
- # Decompose the tuple
- family, localip, localport, desthost, destport = identity
-
- # Check the sockets status
- (exists, status) = nonportable.os_api.exists_outgoing_network_socket(localip,localport,desthost,destport)
-
- # Check if the socket is actively being used
- # If the socket is these states:
- # ESTABLISHED : Connection is active
- # CLOSE_WAIT : Connection is closed, but waiting on local program to close
- # SYN_SENT (SENT) : Connection is just being established
- if exists and ("ESTABLISH" in status or "CLOSE_WAIT" in status or "SENT" in status):
- raise DuplicateTupleError("There is a duplicate connection which conflicts with the request!")
-
- # Otherwise, the socket is being cleaned up
- raise AddressBindingError("Cannot bind to the specified local ip, invalid!")
-
-
-def _conn_cleanup_check(identity):
- """
-
- This private function checks if a socket that
- got EADDRINUSE is because the socket is active,
- or because the socket is listening or
- because the socket is being cleaned up.
-
-
- identity: A tuple to check for cleanup
-
-
- Raises DuplicateTupleError if the socket is actively being used.
-
- Raises AlreadyListeningError if the socket is listening.
-
- Raises CleanupInProgressError if the socket is being cleaned up
- or if the socket does not appear to exist. This is because there
- may be a race between getting EADDRINUSE and the call to this
- function.
-
-
- None
- """
- # Decompose the tuple
- family, localip, localport, desthost, destport = identity
-
- # Check the sockets status
- (exists, status) = nonportable.os_api.exists_outgoing_network_socket(localip,localport,desthost,destport)
-
- # Check if the socket is actively being used
- # If the socket is these states:
- # ESTABLISHED : Connection is active
- # CLOSE_WAIT : Connection is closed, but waiting on local program to close
- # SYN_SENT (SENT) : Connection is just being established
- if exists and ("ESTABLISH" in status or "CLOSE_WAIT" in status or "SENT" in status):
- raise DuplicateTupleError("There is a duplicate connection which conflicts with the request!")
- else:
- # Checking if a listening TCP or UDP socket exists with given local address
- # The third argument is True if socket type is TCP,False if socket type is UDP
- if (nonportable.os_api.exists_listening_network_socket(localip, localport, (family == "TCP"))):
- raise AlreadyListeningError("There is a listening socket on the provided localip and localport!")
- # Otherwise, the socket is being cleaned up
- else:
- raise CleanupInProgressError("The socket is being cleaned up by the operating system!")
-
-
-def _timed_conn_initialize(localip,localport,destip,destport, timeout):
- """
-
- Tries to initialize an outgoing socket to match
- the given address parameters.
-
-
- localip,localport: The local address of the socket
- destip,destport: The destination address to which socket has to be connected
- timeout: Maximum time to try
-
-
- Raises TimeoutError if we timed out trying to connect.
- Raises ConnectionRefusedError if the connection was refused.
- Raises InternetConnectivityError if the network is down.
-
- Raises any errors encountered calling _get_tcp_socket,
- or any non-recoverable network exception.
-
-
- A Python socket object connected to the dest,
- from the specified local tuple.
- """
-
- # Store our start time
- starttime = nonportable.getruntime()
-
- # Get a TCP socket bound to the local ip / port
- sock = _get_tcp_socket(localip, localport)
- sock.settimeout(timeout)
-
- try:
- # Try to connect until we timeout
- connected = False
- while nonportable.getruntime() - starttime < timeout:
- try:
- sock.connect((destip, destport))
- connected = True
- break
- except Exception, e:
- # Check if we are already connected
- if _is_already_connected_exception(e):
- connected = True
- raise DuplicateTupleError("There is a duplicate connection which conflicts with the request!")
- break
-
- # Check if the network is down
- if _is_network_down_exception(e):
- raise InternetConnectivityError("The network is down or cannot be reached from the local IP!")
-
- # Check if the connection was refused
- if _is_conn_refused_exception(e):
- raise ConnectionRefusedError("The connection was refused!")
-
- # Check if this is recoverable (try again, timeout, etc)
- elif not _is_recoverable_network_exception(e):
- raise
-
- # Sleep and retry, avoid busy waiting
- time.sleep(RETRY_INTERVAL)
-
- # Check if we timed out
- if not connected:
- raise TimeoutError("Timed-out connecting to the remote host!")
-
- # Return the socket
- return sock
-
- except:
- # Close the socket, and raise
- sock.close()
- raise
-
-
-# Public interface!!!
-def openconnection(destip, destport,localip, localport, timeout):
- """
-
- Opens a connection, returning a socket-like object
-
-
-
- destip: The destination ip to open communications with
-
- destport: The destination port to use for communication
-
- localip: The local ip to use for the communication
-
- localport: The local port to use for communication
-
- timeout: The maximum amount of time to wait to connect. This may
- be a floating point number or an integer
-
-
-
-
- RepyArgumentError if the arguments are invalid. This includes both
- the types and values of arguments. If the localip matches the destip,
- and the localport matches the destport this will also be raised.
-
- AddressBindingError (descends NetworkError) if the localip isn't
- associated with the local system or is not allowed.
-
- ResourceForbiddenError (descends ResourceError) if the localport isn't
- allowed.
-
- DuplicateTupleError (descends NetworkError) if the (localip, localport,
- destip, destport) tuple is already used. This will also occur if the
- operating system prevents the local IP / port from being used.
-
- AlreadyListeningError if the (localip, localport) tuple is already used
- for a listening TCP socket.
-
- CleanupInProgress if the (localip, localport, destip, destport) tuple is
- still being cleaned up by the OS.
-
- ConnectionRefusedError (descends NetworkError) if the connection cannot
- be established because the destination port isn't being listened on.
-
- TimeoutError (common to all API functions that timeout) if the
- connection times out
-
- InternetConnectivityError if the network is down, or if the host
- cannot be reached from the local IP that has been bound to.
-
-
-
- TODO
-
-
- This operation consumes 64*2 bytes of netsend (SYN, ACK) and 64 bytes
- of netrecv (SYN/ACK). This requires that the localport is allowed. Upon
- success, this call consumes an outsocket.
-
-
- A socket-like object that can be used for communication. Use send,
- recv, and close just like you would an actual socket object in python.
- """
- # Check the input arguments (type)
- if type(destip) is not str:
- raise RepyArgumentError("Provided destip must be a string!")
- if type(localip) is not str:
- raise RepyArgumentError("Provided localip must be a string!")
-
- if type(destport) is not int:
- raise RepyArgumentError("Provided destport must be an int!")
- if type(localport) is not int:
- raise RepyArgumentError("Provided localport must be an int!")
-
- if type(timeout) not in [float, int]:
- raise RepyArgumentError("Provided timeout must be an int or float!")
-
-
- # Check the input arguments (sanity)
- if not _is_valid_ip_address(destip):
- raise RepyArgumentError("Provided destip is not valid! IP: '"+destip+"'")
- if not _is_valid_ip_address(localip):
- raise RepyArgumentError("Provided localip is not valid! IP: '"+localip+"'")
-
- if not _is_valid_network_port(destport):
- raise RepyArgumentError("Provided destport is not valid! Port: "+str(destport))
- if not _is_valid_network_port(localport):
- raise RepyArgumentError("Provided localport is not valid! Port: "+str(localport))
-
- if timeout <= 0:
- raise RepyArgumentError("Provided timeout is not valid, must be positive! Timeout: "+str(timeout))
-
- # Check that if localip == destip, then localport != destport
- if localip == destip and localport == destport:
- raise RepyArgumentError("Local socket name cannot match destination socket name! Local/Dest IP and Port match.")
-
- # Check the input arguments (permission)
- update_ip_cache()
- if not _ip_is_allowed(localip):
- raise ResourceForbiddenError("Provided localip is not allowed! IP: "+localip)
-
- if not _is_allowed_localport("TCP", localport):
- raise ResourceForbiddenError("Provided localport is not allowed! Port: "+str(localport))
-
-
-
- # use this tuple during connection clean up check
- identity = ("TCP", localip, localport, destip, destport)
-
- # Wait for netsend / netrecv
- if _is_loopback_ipaddr(destip):
- nanny.tattle_quantity('loopsend', 0)
- nanny.tattle_quantity('looprecv', 0)
- else:
- nanny.tattle_quantity('netsend', 0)
- nanny.tattle_quantity('netrecv', 0)
-
- try:
- # To Know if remote IP is on loopback or not
- on_loopback = _is_loopback_ipaddr(destip)
-
- # Get the socket
- sock = _timed_conn_initialize(localip,localport,destip,destport, timeout)
-
- # Register this socket as an outsocket
- nanny.tattle_add_item('outsockets',id(sock))
- except Exception, e:
-
- # Check if this an already in use error
- if _is_addr_in_use_exception(e):
- # Call _conn_cleanup_check to determine if this is because
- # the socket is being cleaned up or if it is actively being used
- # This will always raise DuplicateTupleError or
- # CleanupInProgressError or AlreadyListeningError
- _conn_cleanup_check(identity)
-
- # Check if this is a binding error
- if _is_addr_unavailable_exception(e):
- # Call _conn_alreadyexists_check to determine if this is because
- # the connection is active or not
- _conn_alreadyexists_check(identity)
-
-
- # Unknown error...
- else:
- raise
-
- emul_sock = EmulatedSocket(sock, on_loopback)
-
- # Tattle the resources used
- if _is_loopback_ipaddr(destip):
- nanny.tattle_quantity('loopsend', 128)
- nanny.tattle_quantity('looprecv', 64)
- else:
- nanny.tattle_quantity('netsend', 128)
- nanny.tattle_quantity('netrecv', 64)
-
- # Return the EmulatedSocket
- return emul_sock
-
-
-def listenforconnection(localip, localport):
- """
-
- Sets up a TCPServerSocket to recieve incoming TCP connections.
-
-
- localip:
- The local IP to listen on
- localport:
- The local port to listen on
-
-
- Raises AlreadyListeningError if another TCPServerSocket or process has bound
- to the provided localip and localport.
-
- Raises DuplicateTupleError if another process has bound to the
- provided localip and localport.
-
- Raises RepyArgumentError if the localip or localport are invalid
- Raises ResourceForbiddenError if the ip or port is not allowed.
- Raises AddressBindingError if the IP address isn't a local ip.
-
-
- The IP / Port combination cannot be used until the TCPServerSocket
- is closed.
-
-
- Uses an insocket for the TCPServerSocket.
-
-
- A TCPServerSocket object.
- """
- # Check the input arguments (type)
- if type(localip) is not str:
- raise RepyArgumentError("Provided localip must be a string!")
-
- if type(localport) is not int:
- raise RepyArgumentError("Provided localport must be a int!")
-
-
- # Check the input arguments (sanity)
- if not _is_valid_ip_address(localip):
- raise RepyArgumentError("Provided localip is not valid! IP: '"+localip+"'")
-
- if not _is_valid_network_port(localport):
- raise RepyArgumentError("Provided localport is not valid! Port: "+str(localport))
-
-
- # Check the input arguments (permission)
- update_ip_cache()
- if not _ip_is_allowed(localip):
- raise ResourceForbiddenError("Provided localip is not allowed! IP: '"+localip+"'")
-
- if not _is_allowed_localport("TCP", localport):
- raise ResourceForbiddenError("Provided localport is not allowed! Port: "+str(localport))
-
- # This is used to check if there is an existing connection with the same identity
- identity = ("TCP", localip, localport, None, None)
-
- try:
- # Check if localip is on loopback
- on_loopback = _is_loopback_ipaddr(localip)
- # Get the socket
- sock = _get_tcp_socket(localip,localport)
- nanny.tattle_add_item('insockets',id(sock))
- # Get the maximum number of outsockets
- max_outsockets = nanny.get_resource_limit("outsockets")
- # If we have restrictions, then we want to set the outsocket
- # limit
- if max_outsockets:
- # Set the backlog to be the maximum number of outsockets
- sock.listen(max_outsockets)
- else:
- sock.listen(5)
-
- except Exception, e:
-
- # Check if this an already in use error
- if _is_addr_in_use_exception(e):
- # Call _conn_cleanup_check to determine if this is because
- # the socket is being cleaned up or if it is actively being used
- # This will always raise DuplicateTupleError or
- # CleanupInProgressError or AlreadyListeningError
- _conn_cleanup_check(identity)
-
- # Check if this is a binding error
- if _is_addr_unavailable_exception(e):
- # Call _conn_alreadyexists_check to determine if this is because
- # the connection is active or not
- _conn_alreadyexists_check(identity)
- # Unknown error...
- else:
- raise
-
- server_sock = TCPServerSocket(sock, on_loopback)
-
- # Return the TCPServerSocket
- return server_sock
-
-
-# Private method to create a TCP socket and bind
-# to a localip and localport.
-#
-def _get_tcp_socket(localip, localport):
- # Create the TCP socket
- s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- # Reuse the socket if it's "pseudo-availible"
- s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
-
- if localip and localport:
- try:
- s.bind((localip,localport))
- except: # Raise the exception un-tainted
- # don't leak sockets
- s.close()
- raise
- return s
-
-
-# Private method to create a UDP socket and bind
-# to a localip and localport.
-#
-def _get_udp_socket(localip, localport):
- # Create the UDP socket
- s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
- if localip and localport:
- try:
- s.bind((localip, localport))
- except:
- # don't leak sockets
- s.close()
- raise
- return s
-
-
-# Checks if the given real socket would block
-def _check_socket_state(realsock, waitfor="rw", timeout=0.0):
- """
-
- Checks if the given socket would block on a send() or recv().
- In the case of a listening socket, read_will_block equates to
- accept_will_block.
-
-
- realsock:
- A real socket.socket() object to check for.
-
- waitfor:
- An optional specifier of what to wait for. "r" for read only, "w" for write only,
- and "rw" for read or write. E.g. if timeout is 10, and wait is "r", this will block
- for up to 10 seconds until read_will_block is false. If you specify "r", then
- write_will_block is always true, and if you specify "w" then read_will_block is
- always true.
-
- timeout:
- An optional timeout to wait for the socket to be read or write ready.
-
-
- A tuple, (read_will_block, write_will_block).
-
-
- As with select.select(). Probably best to wrap this with _is_recoverable_network_exception
- and _is_terminated_connection_exception. Throws an exception if waitfor is not in ["r","w","rw"]
- """
- # Check that waitfor is valid
- if waitfor not in ["rw","r","w"]:
- raise Exception, "Illegal waitfor argument!"
-
- # Array to hold the socket
- sock_array = [realsock]
-
- # Generate the read/write arrays
- read_array = []
- if "r" in waitfor:
- read_array = sock_array
-
- write_array = []
- if "w" in waitfor:
- write_array = sock_array
-
- # Call select()
- (readable, writeable, exception) = select.select(read_array,write_array,sock_array,timeout)
-
- # If the socket is in the exception list, then assume its both read and writable
- if (realsock in exception):
- return (False, False)
-
- # Return normally then
- return (realsock not in readable, realsock not in writeable)
-
-
-##### Class Definitions
-
-# Public. We pass these to the users for communication purposes
-class EmulatedSocket:
- """
- This object is a wrapper around a tcp
- TCP socket. It allows for sending and
- recieving data, and closing the socket.
-
- It operates in a strictly non-blocking mode,
- and uses Exceptions to indicate when an
- operation would result in blocking behavior.
- """
- # Fields:
- # socket: This is a TCP Socket
- #
- # send_buffer_size: The size of the send buffer. We send less than
- # this to avoid a bug.
- #
- # on_loopback: true if the remote ip is a loopback address.
- # this is used for resource accounting.
- # sock_lock: Threading Lock on socket object used for
- # synchronization.
- __slots__ = ["socketobj", "send_buffer_size", "on_loopback", "sock_lock"]
-
-
- def __init__(self, sock, on_loopback):
- """
-
- Initializes a EmulatedSocket object.
-
-
- sock: A TCP Socket
-
- on_loopback: True/False based on whether remote IP is
- on loopback oe not
-
-
- InteralRepyError is raised if there is no table entry for
- the socket.
-
-
- A EmulatedSocket object.
- """
- # Store the parameters tuple
- self.socketobj = sock
- self.on_loopback = on_loopback
- self.sock_lock = threading.Lock()
-
- # Store the socket send buffer size and set to non-blocking
- self.send_buffer_size = sock.getsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF)
- # locking should be unnecessary because there isn't another external
- # reference here yet
- sock.setblocking(0)
-
-
- def _close(self):
- """
-
- Private close method. Called when socket lock is held.
- Does not perform any accounting / locking. Those should
- be done by the public methods.
-
-
- None
-
-
- Closes the socket
-
-
- None
- """
- # Clean up the socket
- _cleanup_socket(self)
-
- # Replace the socket
- self.socketobj = None
-
-
- def close(self):
- """
-
- Closes a socket. Pending remote recv() calls will return with the
- remaining information. Local recv / send calls will fail after this.
-
-
- None
-
-
- None
-
-
- Pending local recv calls will either return or have an exception.
-
-
- If the connection is closed, no resources are consumed. This operation
- uses 64 bytes of netrecv, and 128 bytes of netsend.
- This call also stops consuming an outsocket.
-
-
- True if this is the first close call to this socket, False otherwise.
- """
- # Get the socket lock
- socket_lock = self.sock_lock
-
- if (self.socketobj == None):
- return False
- # Wait for resources
- if self.on_loopback:
- nanny.tattle_quantity('looprecv', 0)
- nanny.tattle_quantity('loopsend', 0)
- else:
- nanny.tattle_quantity('netrecv', 0)
- nanny.tattle_quantity('netsend', 0)
-
- # Acquire the lock
- socket_lock.acquire()
- try:
- # Internal close
- self._close()
-
- # Tattle the resources
- if self.on_loopback:
- nanny.tattle_quantity('looprecv',64)
- nanny.tattle_quantity('loopsend',128)
- else:
- nanny.tattle_quantity('netrecv',64)
- nanny.tattle_quantity('netsend',128)
-
- # Done
- return True
-
- finally:
- socket_lock.release()
-
-
-
- def recv(self,bytes):
- """
-
- Receives data from a socket. It may receive fewer bytes than
- requested.
-
-
- bytes:
- The maximum number of bytes to read.
-
-
- SocketClosedLocal is raised if the socket was closed locally.
- SocketClosedRemote is raised if the socket was closed remotely.
- SocketWouldBlockError is raised if the socket operation would block.
-
-
- None.
-
-
- This operations consumes 64 + amount of data in bytes
- worth of netrecv, and 64 bytes of netsend.
-
-
- The data received from the socket (as a string). If '' is returned,
- the other side has closed the socket and no more data will arrive.
- """
- # Get the socket lock
- socket_lock = self.sock_lock
- # Wait if already oversubscribed
- if self.on_loopback:
- nanny.tattle_quantity('looprecv',0)
- nanny.tattle_quantity('loopsend',0)
- else:
- nanny.tattle_quantity('netrecv',0)
- nanny.tattle_quantity('netsend',0)
-
-
- # Acquire the socket lock
- socket_lock.acquire()
- try:
- # Get the socket
- sock = self.socketobj
- if sock is None:
- raise KeyError # Socket is closed locally
-
- # Try to recieve the data
- if ((bytes) <= 0):
- return ""
- else:
- data_recieved = sock.recv(bytes)
-
-
-
- # Calculate the length of the data
- data_length = len(data_recieved)
-
- # Raise an exception if there was no data
- if data_length == 0:
- raise SocketClosedRemote("The socket has been closed remotely!")
-
- if self.on_loopback:
- nanny.tattle_quantity('looprecv',data_length+64)
- nanny.tattle_quantity('loopsend',64)
- else:
- nanny.tattle_quantity('netrecv',data_length+64)
- nanny.tattle_quantity('netsend',64)
-
- return data_recieved
-
- except KeyError:
- raise SocketClosedLocal("The socket is closed!")
-
- except RepyException:
- raise # Pass up from inner block
-
- except Exception, e:
- # Check if this a recoverable error
- if _is_recoverable_network_exception(e):
- # Operation would block
- raise SocketWouldBlockError("There is no data! recv() would block.")
-
- elif _is_terminated_connection_exception(e):
- # Remote close
- self._close()
- raise SocketClosedRemote("The socket has been closed remotely!")
-
- else:
- # Unknown error
- self._close()
- raise SocketClosedLocal("The socket has encountered an unexpected error! Error:"+str(e))
-
- finally:
- socket_lock.release()
-
-
-
- def send(self,message):
- """
-
- Sends data on a socket. It may send fewer bytes than requested.
-
-
- message:
- The string to send.
-
-
- SocketClosedLocal is raised if the socket is closed locally.
- SocketClosedRemote is raised if the socket is closed remotely.
- SocketWouldBlockError is raised if the operation would block.
-
-
- None.
-
-
- This operations consumes 64 + size of sent data of netsend and
- 64 bytes of netrecv.
-
-
- The number of bytes sent. Be sure not to assume this is always the
- complete amount!
- """
- # Get the socket lock
- socket_lock = self.sock_lock
- # Wait if already oversubscribed
- if self.on_loopback:
- nanny.tattle_quantity('loopsend',0)
- nanny.tattle_quantity('looprecv',0)
- else:
- nanny.tattle_quantity('netsend',0)
- nanny.tattle_quantity('netrecv',0)
-
- # Trim the message size to be less than the send buffer size.
- # This is a fix for http://support.microsoft.com/kb/823764
- message = message[:self.send_buffer_size-1]
-
- # Acquire the socket lock
- socket_lock.acquire()
- try:
- # Get the socket
- sock = self.socketobj
- if sock is None:
- raise KeyError # Socket is closed locally
-
- # Detect Socket Closed Remote
- # Fixes ticket#974
- (readable, writable, exception) = select.select([sock],[],[],0)
- # check if socket is readable. This is true if the remote end closed.
- if readable:
-
- # if socket is readable but there was no data this means the remote end
- # has closed the socket. We peek so that we don't consume a character.
- data_peeked = sock.recv(1,socket.MSG_PEEK)
- if len(data_peeked) == 0:
- # remote socket is closed
- raise SocketClosedRemote("The socket has been closed by the remote end!")
-
- # Try to send the data
- bytes_sent = sock.send(message)
-
- if self.on_loopback:
- nanny.tattle_quantity('looprecv', 64)
- nanny.tattle_quantity('loopsend', 64 + bytes_sent)
- else:
- nanny.tattle_quantity('netrecv', 64)
- nanny.tattle_quantity('netsend', 64 + bytes_sent)
-
- # Return the number of bytes sent
- return bytes_sent
-
-
- except KeyError:
- raise SocketClosedLocal("The socket is closed!")
- except RepyException:
- raise # pass up from inner block
- except Exception, e:
- # Check if this a recoverable error
- if _is_recoverable_network_exception(e):
- # Operation would block
- raise SocketWouldBlockError("send() would block.")
-
- elif _is_terminated_connection_exception(e):
- # Remote close
- self._close()
- raise SocketClosedRemote("The socket has been closed remotely!")
-
- else:
- # Unknown error
- self._close()
- raise SocketClosedLocal("The socket has encountered an unexpected error! Error:"+str(e))
-
- finally:
- socket_lock.release()
-
-
- def __del__(self):
- # Get the socket lock
- try:
- socket_lock = self.sock_lock
- except KeyError:
- # Closed, done
- return
-
- # Acquire the lock and close
- socket_lock.acquire()
- try:
- self._close()
- finally:
- socket_lock.release()
-
-
-# End of EmulatedSocket class
-
-
-# Public: Class the behaves represents a listening UDP socket.
-class UDPServerSocket:
- """
- This object is a wrapper around a listening
- UDP socket. It allows for accepting incoming
- messages, and closing the socket.
-
- It operates in a strictly non-blocking mode,
- and uses Exceptions to indicate when an
- operation would result in blocking behavior.
- """
- # Fields:
- # sock: This is a listening UDP socket
- # on_loopback: True if the local IP is a loopback address.
- # This is used for resource accounting.
- # sock_lock: Threading Lock on socket object used for
- # synchronization.
- __slots__ = ["socketobj", "on_loopback", "sock_lock"]
-
- # UDP listening socket interface
- def __init__(self, sock, on_loopback):
- """
-
- Initializes the UDPServerSocket. The socket
- should already be established by listenformessage
- prior to calling the initializer.
-
-
-
- socketobj : The listening socket
- on_loopback : True/False based on whether the local IP
- is a loopback address or not
-
- None
-
-
- A UDPServerSocket
- """
- # Store the parameters
- self.socketobj = sock
- self.on_loopback = on_loopback
- self.sock_lock = threading.Lock()
-
- # Set the socket to non-blocking
- # locking should be unnecessary because there isn't another external
- # reference here yet
- sock.setblocking(0)
-
- def getmessage(self):
- """
-
- Obtains an incoming message that was sent to an IP and port.
-
-
- None.
-
-
- SocketClosedLocal if UDPServerSocket.close() was called.
- Raises SocketWouldBlockError if the operation would block.
-
-
- None
-
-
- This operation consumes 64 + size of message bytes of netrecv
-
-
- A tuple consisting of the remote IP, remote port, and message.
-
- """
- # Get the socket lock
-
- socket_lock = self.sock_lock
- # Wait for netrecv resources
- if self.on_loopback:
- nanny.tattle_quantity('looprecv',0)
- else:
- nanny.tattle_quantity('netrecv',0)
-
- # Acquire the lock
- socket_lock.acquire()
- try:
- # Get the socket itself. This must be done after
- # we acquire the lock because it is possible that the
- # socket was closed/re-opened or that it was set to None,
- # etc.
- mysocketobj = self.socketobj
- if mysocketobj is None:
- raise KeyError # Indicates socket is closed
-
- # Try to get a message of any size. (64K is the max that fits in the
- # UDP header)
- message, addr = mysocketobj.recvfrom(65535)
- remote_ip, remote_port = addr
-
- # Do some resource accounting
- if self.on_loopback:
- nanny.tattle_quantity('looprecv', 64 + len(message))
- else:
- nanny.tattle_quantity('netrecv', 64 + len(message))
-
- # Return everything
- return (remote_ip, remote_port, message)
-
- except KeyError:
- # Socket is closed
- raise SocketClosedLocal("The socket has been closed!")
-
- except RepyException:
- # Let these through from the inner block
- raise
-
- except Exception, e:
- # Check if this is a would-block error
- if _is_recoverable_network_exception(e):
- raise SocketWouldBlockError("No messages currently available!")
-
- else:
- # Unexpected, close the socket, and then raise SocketClosedLocal
- _cleanup_socket(self)
- raise SocketClosedLocal("Unexpected error, socket closed!")
-
- finally:
- # Release the lock
- socket_lock.release()
-
-
-
- def close(self):
- """
-
- Closes a socket that is listening for messages.
-
-
- None.
-
-
- None.
-
-
- The IP address and port can be reused by other UDPServerSockets after
- this.
-
-
- If applicable, this operation stops consuming the corresponding
- insocket.
-
-
- True if this is the first close call to this socket, False otherwise.
-
- """
- # Get the socket lock
- socket_lock = self.sock_lock
- # Acquire the lock
- socket_lock.acquire()
- try:
- # Clean up the socket
- _cleanup_socket(self)
- # Replace the socket
- self.socketobj = None
-
- return True
-
- finally:
- socket_lock.release()
-
-
- def __del__(self):
- # Clean up global resources on garbage collection.
- self.close()
-
-
-
-
-class TCPServerSocket (object):
- """
- This object is a wrapper around a listening
- TCP socket. It allows for accepting incoming
- connections, and closing the socket.
-
- It operates in a strictly non-blocking mode,
- and uses Exceptions to indicate when an
- operation would result in blocking behavior.
- """
- # Fields:
- # socket: This is a listening TCP socket
- # sock_lock: Threading Lock on socket object used for
- # synchronization.
- # on_loopback: true if the remote ip is a loopback address.
- # this is used for resource accounting.
- #
-
- __slots__ = ["socketobj", "on_loopback", "sock_lock"]
- def __init__(self, sock, on_loopback):
- """
-
- Initializes the TCPServerSocket. The socket
- should already be established by listenforconnection
- prior to calling the initializer.
-
-
- socketobj: The TCP listening socket
-
- on_loopback: True/False based on whether local IP
- is on loopback or not
-
-
- None
-
-
- A TCPServerSocket
- """
- # Store the parameters
- self.socketobj = sock
- self.sock_lock = threading.Lock()
- self.on_loopback = on_loopback
-
- # Set the socket to non-blocking
- # locking should be unnecessary because there isn't another external
- # reference here yet
- sock.setblocking(0)
-
-
-
- def getconnection(self):
- """
-
- Accepts an incoming connection to a listening TCP socket.
-
-
- None
-
-
- Raises SocketClosedLocal if close() has been called.
- Raises SocketWouldBlockError if the operation would block.
- Raises ResourcesExhaustedError if there are no free outsockets.
-
-
- If successful, consumes 128 bytes of netrecv (64 bytes for
- a SYN and ACK packet) and 64 bytes of netsend (1 ACK packet).
- Uses an outsocket.
-
-
- A tuple containing: (remote ip, remote port, socket object)
- """
- # Get the socket lock
- socket_lock = self.sock_lock
-
- # Wait for netsend and netrecv resources
- if self.on_loopback:
- nanny.tattle_quantity('looprecv',0)
- nanny.tattle_quantity('loopsend',0)
- else:
- nanny.tattle_quantity('netrecv',0)
- nanny.tattle_quantity('netsend',0)
-
- # Acquire the lock
- socket_lock.acquire()
- try:
- # Get the socket itself. This must be done after
- # we acquire the lock because it is possible that the
- # socket was closed/re-opened or that it was set to None,
- # etc.
- socket = self.socketobj
- if socket is None:
- raise KeyError # Indicates socket is closed
-
- # Try to accept
- new_socket, remote_host_info = socket.accept()
- remote_ip, remote_port = remote_host_info
-
- # Get new_socket id to register new_socket with nanny
- new_sockid = id(new_socket)
- # Check if remote_ip is on loopback
- is_on_loopback = _is_loopback_ipaddr(remote_ip)
- # Do some resource accounting
- if self.on_loopback:
- nanny.tattle_quantity('looprecv', 128)
- nanny.tattle_quantity('loopsend', 64)
- else:
- nanny.tattle_quantity('netrecv', 128)
- nanny.tattle_quantity('netsend', 64)
-
- try:
- nanny.tattle_add_item('outsockets', new_sockid)
- except ResourceExhaustedError:
- # Close the socket, and raise
- new_socket.close()
- raise
-
- wrapped_socket = EmulatedSocket(new_socket, is_on_loopback)
-
- # Return everything
- return (remote_ip, remote_port, wrapped_socket)
-
- except KeyError:
- # Socket is closed
- raise SocketClosedLocal("The socket has been closed!")
-
- except RepyException:
- # Let these through from the inner block
- raise
-
- except Exception, e:
- # Check if this is a would-block error
- if _is_recoverable_network_exception(e):
- raise SocketWouldBlockError("No connections currently available!")
-
- else:
- # Unexpected, close the socket, and then raise SocketClosedLocal
- _cleanup_socket(self)
- raise SocketClosedLocal("Unexpected error, socket closed!")
-
- finally:
- # Release the lock
- socket_lock.release()
-
-
- def close(self):
- """
-
- Closes the listening TCP socket.
-
-
- None
-
-
- None
-
-
- The IP and port can be re-used after closing.
-
-
- Releases the insocket used.
-
-
- True, if this is the first call to close.
- False otherwise.
- """
- # Get the socket lock
- socket_lock = self.sock_lock
-
- # Acquire the lock
- socket_lock.acquire()
- try:
- # Clean up the socket
- _cleanup_socket(self)
- # Replace the socket
- self.socketobj = None
- # Done
- return True
-
- finally:
- socket_lock.release()
-
-
- def __del__(self):
- # Close the socket
- self.close()
-
diff --git a/error.log b/error.log
deleted file mode 100644
index c6be7ac..0000000
--- a/error.log
+++ /dev/null
@@ -1,21 +0,0 @@
-root@abhishek-VirtualBox:/var/log/apache2# tail -20f error.log
-[Fri Nov 14 06:12:08.023144 2014] [:error] [pid 12710:tid 3043838784] [remote 10.0.2.15:52662] return self.format('D, j M Y H:i:s O')
-[Fri Nov 14 06:12:08.023159 2014] [:error] [pid 12710:tid 3043838784] [remote 10.0.2.15:52662] File "/usr/local/lib/python2.7/dist-packages/django/utils/dateformat.py", line 35, in format
-[Fri Nov 14 06:12:08.023181 2014] [:error] [pid 12710:tid 3043838784] [remote 10.0.2.15:52662] pieces.append(force_text(getattr(self, piece)()))
-[Fri Nov 14 06:12:08.023197 2014] [:error] [pid 12710:tid 3043838784] [remote 10.0.2.15:52662] File "/usr/local/lib/python2.7/dist-packages/django/utils/encoding.py", line 100, in force_text
-[Fri Nov 14 06:12:08.023354 2014] [:error] [pid 12710:tid 3043838784] [remote 10.0.2.15:52662] s = s.__unicode__()
-[Fri Nov 14 06:12:08.023375 2014] [:error] [pid 12710:tid 3043838784] [remote 10.0.2.15:52662] File "/usr/local/lib/python2.7/dist-packages/django/utils/functional.py", line 138, in __text_cast
-[Fri Nov 14 06:12:08.023559 2014] [:error] [pid 12710:tid 3043838784] [remote 10.0.2.15:52662] return func(*self.__args, **self.__kw)
-[Fri Nov 14 06:12:08.023580 2014] [:error] [pid 12710:tid 3043838784] [remote 10.0.2.15:52662] File "/usr/local/lib/python2.7/dist-packages/django/utils/translation/__init__.py", line 76, in ugettext
-[Fri Nov 14 06:12:08.023699 2014] [:error] [pid 12710:tid 3043838784] [remote 10.0.2.15:52662] return _trans.ugettext(message)
-[Fri Nov 14 06:12:08.023719 2014] [:error] [pid 12710:tid 3043838784] [remote 10.0.2.15:52662] File "/usr/local/lib/python2.7/dist-packages/django/utils/translation/trans_real.py", line 281, in ugettext
-[Fri Nov 14 06:12:08.029585 2014] [:error] [pid 12710:tid 3043838784] [remote 10.0.2.15:52662] return do_translate(message, 'ugettext')
-[Fri Nov 14 06:12:08.029680 2014] [:error] [pid 12710:tid 3043838784] [remote 10.0.2.15:52662] File "/usr/local/lib/python2.7/dist-packages/django/utils/translation/trans_real.py", line 263, in do_translate
-[Fri Nov 14 06:12:08.029764 2014] [:error] [pid 12710:tid 3043838784] [remote 10.0.2.15:52662] _default = translation(settings.LANGUAGE_CODE)
-[Fri Nov 14 06:12:08.029802 2014] [:error] [pid 12710:tid 3043838784] [remote 10.0.2.15:52662] File "/usr/local/lib/python2.7/dist-packages/django/utils/translation/trans_real.py", line 177, in translation
-[Fri Nov 14 06:12:08.029848 2014] [:error] [pid 12710:tid 3043838784] [remote 10.0.2.15:52662] default_translation = _fetch(settings.LANGUAGE_CODE)
-[Fri Nov 14 06:12:08.029883 2014] [:error] [pid 12710:tid 3043838784] [remote 10.0.2.15:52662] File "/usr/local/lib/python2.7/dist-packages/django/utils/translation/trans_real.py", line 159, in _fetch
-[Fri Nov 14 06:12:08.029925 2014] [:error] [pid 12710:tid 3043838784] [remote 10.0.2.15:52662] app = import_module(appname)
-[Fri Nov 14 06:12:08.029958 2014] [:error] [pid 12710:tid 3043838784] [remote 10.0.2.15:52662] File "/usr/local/lib/python2.7/dist-packages/django/utils/importlib.py", line 40, in import_module
-[Fri Nov 14 06:12:08.030190 2014] [:error] [pid 12710:tid 3043838784] [remote 10.0.2.15:52662] __import__(name)
-[Fri Nov 14 06:12:08.030250 2014] [:error] [pid 12710:tid 3043838784] [remote 10.0.2.15:52662] ImportError: No module named django_pph
diff --git a/experimentlib.py b/experimentlib.py
deleted file mode 100644
index 8d76c03..0000000
--- a/experimentlib.py
+++ /dev/null
@@ -1,1888 +0,0 @@
-"""
-
- experimentlib.py
-
-
- Justin Samuel
-
-
- December 1, 2009
-
-
- A library for conducting experiments using Seattle vessels. The functions in
- this library allow for communicating with vessels (e.g. to upload files and
- run programs) as well as for communicating with SeattleGENI (e.g. to obtain
- vessels to run experiments on).
-
-
- Ensure that this file is in a directory containing the seattlelib files as
- well as the seattleclearinghouse_xmlrpc.py module. In your own script, add:
-
- import experimentlib
-
- then call the methods desired.
-
- Note that if your script resides outside of the directory that contains the
- seattlelib files, experimentlib.py, and seattlegeni_client.py, then you'll
- need to set that directory/those directories in your python path. For example,
- if you downloaded an installer (even if you haven't installed Seattle on the
- machine this script resides on, the path will be to the seattle_repy directory
- that was among the extracted installer files. To set the path directly in your
- script rather than through environment variables, you can use something like:
-
- import sys
- sys.path.append("/path/to/seattle_repy")
-
- You would need to do the above *before* your line that says:
-
- import experimentlib
-
- For examples of using this experimentlib, see the examples/ directory.
-
- Please also see the following wiki page for usage information and how to
- obtain the latest version of this experiment library:
-
- https://seattle.cs.washington.edu/wiki/ExperimentLibrary
-
-
-
- Object Definitions:
-
- * identity: a dictionary that minimally contains a public key but may also
- contain the related private key and the username of a corresponding
- SeattleGENI account. When one wants to perform any operation that would
- require a public key, private key, or username, an identity must be
- provided. An identity can be created using the functions named
- create_identity_from_*.
-
- * vesselhandle: a vesselhandle is a string that contains the information
- required to uniquely identify a vessel, regardless of the current
- location (IP address) of the node the vessel is on. This is in the format
- of "nodeid:vesselname".
-
- * nodeid: a string that contains the information required to uniquely
- identify a node, regardless of its current location.
-
- * vesselname: a string containing the name of a vessel. This name will
- be unique on any given node, but the same name is likely is used for
- vessels on other nodes. Thus, this does not uniquely identify a vessel,
- in general. To uniquely identify a vessel, a vesselhandle is needed.
-
- * nodelocation: a string containing the location of a node. This will not
- always be "ip:port". It could, for example, be "NATid:port" in the case
- of a node that is accessible through a NAT forwarder.
-
- * vesseldict: a dictionary of details related to a given vessel. The keys
- that will always be present are 'vesselhandle', 'nodelocation',
- 'vesselname', and 'nodeid'. Additional keys will be present depending on
- the function that returns the vesseldict. See the individual function
- docstring for details.
-
- Exceptions:
-
- All exceptions raised by functions in this module will either be or extend:
- * SeattleExperimentError
- * SeattleClearinghouseError
-
- The SeattleClearinghouseError* exceptions will only be raised by the functions whose
- names are seattlegeni_*. Any of the seattlegeni_* functions may raise the
- following in addition to specific exceptions described in the function
- docstrings (these are all subclasses of SeattleClearinghouseError):
- * SeattleClearinghouseCommunicationError
- * SeattleClearinghouseAuthenticationError
- * SeattleClearinghouseInvalidRequestError
- * SeattleClearinghouseInternalError
-
- In the case of invalid arguments to functions, the following may be
- raised (these will not always be documented for each function):
- * TypeError
- * ValueError
- * IOError (if the function involves reading/writing files and the
- filename provided is missing/unreadable/unwritable)
-
- For the specific exceptions raised by a given function, see the function's
- docstring.
-"""
-
-import os
-import random
-import time
-import tracebackrepy
-import xmlrpclib
-
-import seattleclearinghouse_xmlrpc
-
-# We use a helper module to do repy module imports so that we don't import
-# unexpected items into this module's namespace. This helps reduce errors
-# because editors/pylint make it clear when an unknown identifier is used
-# and it also makes other things easier for developers, such as using ipython's
-# tab completion and not causing unexpected imports if someone using this
-# module decides to use "from experimentlib import *"
-#import repyimporter
-
-import fastnmclient
-"""
-repytime = repyimporter.import_repy_module("time")
-rsa = repyimporter.import_repy_module("rsa")
-parallelize = repyimporter.import_repy_module("parallelize")
-advertise = repyimporter.import_repy_module("advertise")
-"""
-from repyportability import add_dy_support
-add_dy_support(locals())
-
-repytime = dy_import_module_symbols("time.r2py")
-rsa = dy_import_module_symbols("rsa.r2py")
-parallelize = dy_import_module_symbols("parallelize.r2py")
-advertise = dy_import_module_symbols("advertise.r2py")
-
-# The maximum number of node locations to return from a call to lookup_node_locations.
-max_lookup_results = 1024 * 1024
-
-# The timeout to use for communication, both in advertisement lookups and for
-# contacting nodes directly.
-defaulttimeout = 10
-
-# The number of worker threads to use for each parallelized operation.
-num_worker_threads = 5
-
-# Whether additional information and debugging messages should be printed
-# to stderr by this library.
-print_debug_messages = True
-
-# OpenDHT can be slow/hang, which isn't fun if the experimentlib is being used
-# interactively. So, let's default to central advertise server lookups here
-# until we're sure all issues with OpenDHT are resolved.
-# A value of None indicates the default of ['opendht', 'central'].
-#advertise_lookup_types = None
-advertise_lookup_types = ['central']
-
-# A few options to be passed along to the SeattleGENI xmlrpc client.
-# None means the default.
-SEATTLECLEARINGHOUSE_XMLRPC_URL = None
-SEATTLECLEARINGHOUSE_ALLOW_SSL_INSECURE = None # Set to True to allow insecure SSL.
-SEATTLECLEARINGHOUSE_CA_CERTS_FILES = None
-
-# These constants can be used as the type argument to seattlegeni_acquire_vessels.
-SEATTLECLEARINGHOUSE_VESSEL_TYPE_WAN = "wan"
-SEATTLECLEARINGHOUSE_VESSEL_TYPE_LAN = "lan"
-SEATTLECLEARINGHOUSE_VESSEL_TYPE_NAT = "nat"
-SEATTLECLEARINGHOUSE_VESSEL_TYPE_RAND = "rand"
-
-# Some of these vessel status explanations are from:
-# https://seattle.cs.washington.edu/wiki/NodeManagerDesign
-
-# Fresh: has never been started.
-VESSEL_STATUS_FRESH = "Fresh"
-
-# Started: has been started and is running when last checked.
-VESSEL_STATUS_STARTED = "Started"
-
-# Stopped: was running but stopped by NM command
-VESSEL_STATUS_STOPPED = "Stopped"
-
-# Stale: it last reported a start of "Started" but significant time has
-# elapsed, likely due to a system crash (what does "system crash" mean?).
-VESSEL_STATUS_STALE = "Stale"
-
-# Terminated (the vessel stopped of its own volition, possibly due to an error)
-VESSEL_STATUS_TERMINATED = "Terminated"
-
-# The node is not advertising
-VESSEL_STATUS_NO_SUCH_NODE = "NO_SUCH_NODE"
-
-# The node can be communicated with but the specified vessel doesn't exist
-# on the node. This will also be used when the vessel exists on the node but
-# the identity being used is not a user or the owner of the vessel.
-VESSEL_STATUS_NO_SUCH_VESSEL = "NO_SUCH_VESSEL"
-
-# The node can't be communicated with or communication fails.
-VESSEL_STATUS_NODE_UNREACHABLE = "NODE_UNREACHABLE"
-
-# For convenience we define two sets of vessel status constants that include
-# all possible statuses grouped by whether the status indicates the vessel is
-# usable/active or whether it is unusable/inactive.
-VESSEL_STATUS_SET_ACTIVE = set([VESSEL_STATUS_FRESH, VESSEL_STATUS_STARTED,
- VESSEL_STATUS_STOPPED, VESSEL_STATUS_STALE,
- VESSEL_STATUS_TERMINATED])
-VESSEL_STATUS_SET_INACTIVE = set([VESSEL_STATUS_NO_SUCH_NODE, VESSEL_STATUS_NO_SUCH_VESSEL,
- VESSEL_STATUS_NODE_UNREACHABLE])
-
-# Whether _initialize_time() has been called.
-_initialize_time_called = False
-
-# Keys are node locations (a string of "host:port"), values are nmhandles.
-# Note that this method of caching nmhandles will cause problems if multiple
-# identities/keys are being used to contact the name node.
-_nmhandle_cache = {}
-
-# Keys are nodeids, values are nodelocations.
-_node_location_cache = {}
-
-
-
-
-
-class SeattleExperimentError(Exception):
- """Base class for other exceptions."""
-
-
-
-class UnexpectedVesselStatusError(SeattleExperimentError):
- """
- When a vessel status is reported by a node and that status is something
- we don't understand. Mostly this is something we care about because we
- want to definitely tell users what to expect in their code in terms of
- status, so we should be very clear about the possibly values and never
- have to raise this exception.
- """
-
-
-
-class NodeCommunicationError(SeattleExperimentError):
- """Unable to perform a requested action on/communication with a node/vessel."""
-
-
-
-class NodeLocationLookupError(SeattleExperimentError):
- """
- Unable to determine the location of a node based on its nodeid or unable
- to successfully perform an advertisement lookup.
- """
-
-
-
-class NodeLocationNotAdvertisedError(NodeLocationLookupError):
- """
- A lookup was successful but no node locations are being advertised under a
- nodeid.
- """
-
-
-
-class UnableToPerformLookupError(NodeLocationLookupError):
- """
- Something is wrong with performing lookups. Either none of the lookup
- services that were tried were successful or there's a bug in some underlying
- code being used by this module.
- """
-
-
-
-class IdentityInformationMissingError(SeattleExperimentError):
- """
- The information that is part of an identity object is incomplete. For
- example, if only the public key is in the identity but the identity is
- used in a way that requires a private key, this exception would be
- raised.
- """
-
-
-#This is the base class for all SeattleGENI errors. We make this available
-#in the namespace of the experimentlib so that clients do not have to import
-#seattleclearinghouse_xmlrpc to catch these.
-SeattleClearinghouseError = seattleclearinghouse_xmlrpc.SeattleClearinghouseError
-
-# We make these available, as well, in case users find them useful. We prefix
-# all of these error names with SeattleGENI.
-SeattleClearinghouseCommunicationError = seattleclearinghouse_xmlrpc.CommunicationError
-SeattleClearinghouseInternalError = seattleclearinghouse_xmlrpc.InternalError
-SeattleClearinghouseAuthenticationError = seattleclearinghouse_xmlrpc.AuthenticationError
-SeattleClearinghouseInvalidRequestError = seattleclearinghouse_xmlrpc.InvalidRequestError
-SeattleClearinghouseNotEnoughCreditsError = seattleclearinghouse_xmlrpc.NotEnoughCreditsError
-SeattleClearinghouseUnableToAcquireResourcesError = seattleclearinghouse_xmlrpc.UnableToAcquireResourcesError
-
-
-
-
-
-def _validate_vesselhandle(vesselhandle):
- if not isinstance(vesselhandle, basestring):
- raise TypeError("vesselhandle must be a string, not a " + str(type(vesselhandle)))
-
- parts = vesselhandle.split(':')
- if len(parts) != 2:
- raise ValueError("invalid vesselhandle '" + vesselhandle + "', should be nodeid:vesselname")
-
-
-
-
-
-def _validate_vesselhandle_list(vesselhandle_list):
- if not isinstance(vesselhandle_list, list):
- raise TypeError("vesselhandle list must be a list, not a " + str(type(vesselhandle_list)))
-
- for vesselhandle in vesselhandle_list:
- _validate_vesselhandle(vesselhandle)
-
-
-
-
-
-def _validate_nodelocation(nodelocation):
- if not isinstance(nodelocation, basestring):
- raise TypeError("nodelocation must be a string, not a " + str(type(nodelocation)))
-
- parts = nodelocation.split(':')
- if len(parts) != 2:
- raise ValueError("nodelocation '" + nodelocation + "' invalid, should be host:port")
-
-
-
-
-
-def _validate_nodelocation_list(nodelocation_list):
- if not isinstance(nodelocation_list, list):
- raise TypeError("nodelocation list must be a list, not a " + str(type(nodelocation_list)))
-
- for nodelocation in nodelocation_list:
- _validate_nodelocation(nodelocation)
-
-
-
-
-
-def _validate_identity(identity, require_private_key=False, require_username=False):
- if not isinstance(identity, dict):
- raise TypeError("identity must be a dict, not a " + str(type(identity)))
-
- if 'publickey_str' not in identity:
- raise TypeError("identity dict doesn't have a 'publickey_str' key, so it's not an identity.")
-
- if require_private_key:
- if 'privatekey_str' not in identity:
- raise IdentityInformationMissingError("identity must have a private key for the requested operation.")
-
- if require_username:
- if 'username' not in identity:
- raise IdentityInformationMissingError("identity must have a username for the requested operation.")
-
-
-
-
-
-def _initialize_time():
- """
- Does its best to call time_updatetime() and raises a SeattleExperimentError
- if it doesn't succeed after many tries.
- """
- global _initialize_time_called
-
- if not _initialize_time_called:
-
- max_attempts = 10
- possible_ports = range(10000, 60001)
-
- # Ports to use for UDP listening when doing a time update.
- portlist = random.sample(possible_ports, max_attempts)
-
- for localport in portlist:
- try:
- repytime.time_updatetime(localport)
- _initialize_time_called = True
- return
- except repytime.TimeError:
- error_message = tracebackrepy.format_exception()
-
- raise SeattleExperimentError("Failed to perform time_updatetime(): " + error_message)
-
-
-
-
-
-def _create_list_from_key_in_dictlist(dictlist, key):
- """
- List comprehensions are verboten by our coding style guide (generally for
- good reason). Otherwise, we wouldn't have this function and would just write
- the following wherever needed:
- [x[key] for x in dictlist]
- """
- new_list = []
- for dictitem in dictlist:
- new_list.append(dictitem[key])
- return new_list
-
-
-
-
-
-def _get_nmhandle(nodelocation, identity=None):
- """
- Get an nmhandle for the nodelocation and identity, if provided. This will look
- use a cache of nmhandles and only create a new one if the requested nmhandle
- has not previously been requested.
- """
-
- # Call _initialize_time() here because time must be updated at least once before
- # nmhandles are used.
- _initialize_time()
-
- host, port = nodelocation.split(':')
- port = int(port)
-
- if identity is None:
- identitystring = "None"
- else:
- identitystring = identity['publickey_str']
-
- if identitystring not in _nmhandle_cache:
- _nmhandle_cache[identitystring] = {}
-
- if nodelocation not in _nmhandle_cache[identitystring]:
- try:
- if identity is None:
- nmhandle = fastnmclient.nmclient_createhandle(host, port, timeout=defaulttimeout)
- elif 'privatekey_dict' in identity:
- nmhandle = fastnmclient.nmclient_createhandle(host, port, privatekey=identity['privatekey_dict'],
- publickey=identity['publickey_dict'], timeout=defaulttimeout)
- else:
- nmhandle = fastnmclient.nmclient_createhandle(host, port, publickey=identity['publickey_dict'],
- timeout=defaulttimeout)
- except fastnmclient.NMClientException, e:
- raise NodeCommunicationError(str(e))
-
- _nmhandle_cache[identitystring][nodelocation] = nmhandle
-
- return _nmhandle_cache[identitystring][nodelocation]
-
-
-
-
-
-def run_parallelized(targetlist, func, *args):
- """
-
- Parallelize the calling of a given function using multiple threads.
-
- targetlist
- a list what will be the first argument to func each time it is called.
- func
- the function to be called once for each item in targetlist.
- *args
- (optional) every additional argument will be passed to func after an
- item from targetlist. That is, these will be the second, third, etc.
- argument to func, if provided. These are not required a.
-
- SeattleExperimentError
- Raised if there is a problem performing parallel processing. This will
- not be raised just because func raises exceptions. If func raises
- exceptions when it is called, that exception information will be
- available through the run_parallelized's return value.
-
- Up to num_worker_threads (a global variable) threads will be spawned to
- call func once for every item in targetlist.
-
- A tuple of:
- (successlist, failurelist)
- where successlist is a list of tuples of the format:
- (target, return_value_from_func)
- and failurelist is a list of tuples of the format:
- (target, exception_string)
- Note that exception_string will not contain a full traceback, but rather
- only the string representation of the exception.
- """
-
- try:
- phandle = parallelize.parallelize_initfunction(targetlist, func, num_worker_threads, *args)
-
- while not parallelize.parallelize_isfunctionfinished(phandle):
- # TODO: Give up after a timeout? This seems risky as run_parallelized may
- # be used with functions that take a long time to complete and very large
- # lists of targets. It would be a shame to break a user's program because
- # of an assumption here. Maybe it should be an optional argument to
- # run_parallelized.
- time.sleep(.1)
-
- results = parallelize.parallelize_getresults(phandle)
- except parallelize.ParallelizeError:
- raise SeattleExperimentError("Error occurred in run_parallelized: " +
- tracebackrepy.format_exception())
- finally:
- parallelize.parallelize_closefunction(phandle)
-
- # These are lists of tuples. The first is a list of (target, retval), the
- # second is a list of (target, errormsg)
- return results['returned'], results['exception']
-
-
-
-
-
-
-def create_identity_from_key_files(publickey_fn, privatekey_fn=None):
- """
-
- Create an identity from key files.
-
- publickey_fn
- The full path, including filename, to the public key this identity
- should represent. Note that the identity's username will be assumed
- to be the part of the base filename before the first period (or the
- entire base filename if there is no period). So, to indicate a username
- of "joe", the filename should be, for example, "joe.publickey".
- privatekey_fn
- (optional) The full path, including filename, to the private key that
- corresponds to publickey_fn. If this is not provided, then the identity
- will not be able to be used for operations the require a private key.
-
- IOError
- if the files do not exist or are not readable.
- ValueError
- if the files do not contain valid keys.
-
- An identity object to be used with other functions in this module.
- """
- identity = {}
- identity["username"] = os.path.basename(publickey_fn).split(".")[0]
- identity["publickey_fn"] = publickey_fn
- try:
- identity["publickey_dict"] = rsa.rsa_file_to_publickey(publickey_fn)
- identity["publickey_str"] = rsa.rsa_publickey_to_string(identity["publickey_dict"])
-
- if privatekey_fn is not None:
- identity["privatekey_fn"] = privatekey_fn
- identity["privatekey_dict"] = rsa.rsa_file_to_privatekey(privatekey_fn)
- identity["privatekey_str"] = rsa.rsa_privatekey_to_string(identity["privatekey_dict"])
- except IOError:
- raise
- except ValueError:
- raise
-
- return identity
-
-
-
-
-
-def create_identity_from_key_strings(publickey_string, privatekey_string=None, username=None):
- """
-
- Create an identity from key strings.
-
- publickey_string
- The string containing the public key this identity should represent. The
- string must consists of the modulus, followed by a space, followed by
- the public exponent. This will be the same as the contents of a public
- key file.
- privatekey_string
- (optional) The full path, including filename, to the private key that
- corresponds to publickey_fn. If this is not provided, then the identity
- will not be able to be used for operations the require a private key.
- username
- (optional) A string containing the username to associate with this
- identity. This is only necessary if using this identity with the
- seattlegeni_* functions.
-
- ValueError
- if the strings do not contain valid keys.
-
- An identity object to be used with other functions in this module.
- """
- identity = {}
- identity["username"] = username
- try:
- identity["publickey_dict"] = rsa.rsa_string_to_publickey(publickey_string)
- identity["publickey_str"] = rsa.rsa_publickey_to_string(identity["publickey_dict"])
-
- if privatekey_string is not None:
- identity["privatekey_dict"] = rsa.rsa_string_to_privatekey(privatekey_string)
- identity["privatekey_str"] = rsa.rsa_privatekey_to_string(identity["privatekey_dict"])
- except IOError:
- # Raised if there is a problem reading the file.
- raise
- except ValueError:
- # Raised by the repy rsa module when the key is invald.
- raise
-
- return identity
-
-
-
-
-
-def _lookup_node_locations(keystring, lookuptype=None):
- """Does the actual work of an advertise lookup."""
-
- keydict = rsa.rsa_string_to_publickey(keystring)
- try:
- if lookuptype is not None:
- nodelist = advertise.advertise_lookup(keydict, maxvals=max_lookup_results, timeout=defaulttimeout, lookuptype=lookuptype)
- else:
- nodelist = advertise.advertise_lookup(keydict, maxvals=max_lookup_results, timeout=defaulttimeout)
- except advertise.AdvertiseError, e:
- raise UnableToPerformLookupError("Failure when trying to perform advertise lookup: " +
- tracebackrepy.format_exception())
-
- # If there are no vessels for a user, the lookup may return ''.
- for nodename in nodelist[:]:
- if nodename == '':
- nodelist.remove(nodename)
-
- return nodelist
-
-
-
-
-
-def lookup_node_locations_by_identity(identity):
- """
-
- Lookup the locations of nodes that are advertising their location under a
- specific identity's public key.
-
- identity
- The identity whose public key should be used to lookup nodelocations.
-
- UnableToPerformLookupError
- If a failure occurs when trying lookup advertised node locations.
-
- A list of nodelocations.
- """
- _validate_identity(identity)
- keystring = str(identity['publickey_str'])
- return _lookup_node_locations(keystring, lookuptype=advertise_lookup_types)
-
-
-
-
-
-def lookup_node_locations_by_nodeid(nodeid):
- """
-
- Lookup the locations that a specific node has advertised under. There may
- be multiple locations advertised if the node has recently changed location.
-
- nodeid
- The nodeid of the node whose advertised locations are to be looked up.
-
- UnableToPerformLookupError
- If a failure occurs when trying lookup advertised node locations.
-
- A list of nodelocations.
- """
- return _lookup_node_locations(nodeid, lookuptype=advertise_lookup_types)
-
-
-
-
-
-def find_vessels_on_nodes(identity, nodelocation_list):
- """
-
- Contact one or more nodes and determine which vessels on those nodes are
- usable by a given identity.
-
- identity
- The identity whose vessels we are interested in. This can be the identity
- of either the vessel owner or a vessel user.
- nodelocation_list
- A list of nodelocations that should be contacted. This can be an empty
- list (which will result in an empty list of vesselhandles returned).
-
- SeattleExperimentError
- If an error occurs performing a parallelized operation.
-
- A list of vesselhandles.
- """
- _validate_identity(identity)
- _validate_nodelocation_list(nodelocation_list)
-
- successlist, failurelist = run_parallelized(nodelocation_list, browse_node, identity)
-
- vesseldicts = []
-
- for (nodeid, vesseldicts_of_node) in successlist:
- vesseldicts += vesseldicts_of_node
-
- return _create_list_from_key_in_dictlist(vesseldicts, "vesselhandle")
-
-
-
-
-
-def browse_node(nodelocation, identity=None):
- """
-
- Contact an individual node to gather detailed information about all of the
- vessels on the node that are usable by a given identity.
-
- nodelocation
- The nodelocation of the node that should be browsed.
- identity
- (optional) The identity whose vessels we are interested in. This can be
- the identity of either the vessel owner or a vessel user. If None,
- then the vesseldicts for all vessels on the node will be returned.
-
- NodeCommunicationError
- If the communication with the node fails for any reason, including the
- node not being reachable, timeout in communicating with the node, the
- node rejecting the
-
- A list of vesseldicts. Each vesseldict contains the additional keys:
- 'status'
- The status string of the vessel.
- 'ownerkey'
- The vessel's owner key (in dict format).
- 'userkeys'
- A list of the vessel's user keys (each in dict format).
- """
- try:
- _validate_nodelocation(nodelocation)
- if identity is not None:
- _validate_identity(identity)
-
- nmhandle = _get_nmhandle(nodelocation, identity)
- try:
- nodeinfo = fastnmclient.nmclient_getvesseldict(nmhandle)
- except fastnmclient.NMClientException, e:
- raise NodeCommunicationError("Failed to communicate with node " + nodelocation + ": " + str(e))
-
- # We do our own looking through the nodeinfo rather than use the function
- # nmclient_listaccessiblevessels() as we don't want to contact the node a
- # second time.
- usablevessels = []
- for vesselname in nodeinfo['vessels']:
- if identity is None:
- usablevessels.append(vesselname)
- elif identity['publickey_dict'] == nodeinfo['vessels'][vesselname]['ownerkey']:
- usablevessels.append(vesselname)
- elif 'userkeys' in nodeinfo['vessels'][vesselname] and \
- identity['publickey_dict'] in nodeinfo['vessels'][vesselname]['userkeys']:
- usablevessels.append(vesselname)
-
- nodeid = rsa.rsa_publickey_to_string(nodeinfo['nodekey'])
- # For efficiency, let's update the _node_location_cache with this info.
- # This can prevent individual advertise lookups of each nodeid by other
- # functions in the experimentlib that may be called later.
- _node_location_cache[nodeid] = nodelocation
-
- vesseldict_list = []
- for vesselname in usablevessels:
- vesseldict = {}
- # Required keys in vesseldicts (see the module comments for more info).
- vesseldict['vesselhandle'] = nodeid + ":" + vesselname
- vesseldict['nodelocation'] = nodelocation
- vesseldict['vesselname'] = vesselname
- vesseldict['nodeid'] = nodeid
- # Additional keys that browse_node provides.
- vesseldict['status'] = nodeinfo['vessels'][vesselname]['status']
- vesseldict['ownerkey'] = nodeinfo['vessels'][vesselname]['ownerkey']
- vesseldict['userkeys'] = nodeinfo['vessels'][vesselname]['userkeys']
- vesseldict['version'] = nodeinfo['version']
- vesseldict_list.append(vesseldict)
-
- return vesseldict_list
-
- except Exception, e:
- # Useful for debugging during development of the experimentlib.
- #traceback.print_exc()
- raise
-
-
-
-
-
-def get_vessel_status(vesselhandle, identity):
- """
-
- Determine the status of a vessel.
-
- vesselhandle
- The vesselhandle of the vessel whose status is to be checked.
- identity
- The identity of the owner or a user of the vessel.
-
- UnexpectedVesselStatusError
- If the status returned by the node for the vessel is not a status value
- that this experimentlib expects.
-
- The node the vessel is on is communicated with.
-
- A string that is one of the VESSEL_STATUS_* constants.
- """
- _validate_vesselhandle(vesselhandle)
- _validate_identity(identity)
-
- # Determine the last known location of the node.
- nodeid, vesselname = vesselhandle.split(":")
- try:
- # This will get a cached node location if one exists.
- nodelocation = get_node_location(nodeid)
- except NodeLocationNotAdvertisedError, e:
- return VESSEL_STATUS_NO_SUCH_NODE
-
- try:
- vesselinfolist = browse_node(nodelocation, identity)
- except NodeCommunicationError:
- # Do a non-cache lookup of the nodeid to see if the node moved.
- try:
- nodelocation = get_node_location(nodeid, ignorecache=True)
- except NodeLocationNotAdvertisedError, e:
- return VESSEL_STATUS_NO_SUCH_NODE
-
- # Try to communicate again.
- try:
- vesselinfolist = browse_node(nodelocation, identity)
- except NodeCommunicationError, e:
- return VESSEL_STATUS_NODE_UNREACHABLE
-
- for vesselinfo in vesselinfolist:
- if vesselinfo['vesselhandle'] == vesselhandle:
- # The node is up and the vessel must have the identity's key as the owner
- # or a user, but the status returned isn't one of the statuses we
- # expect. If this does occur, it may indicate a bug in the experiment
- # library where it doesn't know about all possible status a nodemanager
- # may return for a vessel.
- if vesselinfo['status'] not in VESSEL_STATUS_SET_ACTIVE:
- raise UnexpectedVesselStatusError(vesselinfo['status'])
- else:
- return vesselinfo['status']
- else:
- # The node is up but this vessel doesn't exist.
- return VESSEL_STATUS_NO_SUCH_VESSEL
-
-
-
-
-
-def _do_public_node_request(nodeid, requestname, *args):
- nodelocation = get_node_location(nodeid)
- nmhandle = _get_nmhandle(nodelocation)
-
- try:
- return fastnmclient.nmclient_rawsay(nmhandle, requestname, *args)
- except fastnmclient.NMClientException, e:
- raise NodeCommunicationError(str(e))
-
-
-
-
-
-def _do_signed_vessel_request(identity, vesselhandle, requestname, *args):
- _validate_identity(identity, require_private_key=True)
-
- nodeid, vesselname = vesselhandle.split(':')
- nodelocation = get_node_location(nodeid)
- nmhandle = _get_nmhandle(nodelocation, identity)
-
- try:
- return fastnmclient.nmclient_signedsay(nmhandle, requestname, vesselname, *args)
- except fastnmclient.NMClientException, e:
- raise NodeCommunicationError(str(e))
-
-
-
-
-
-def get_node_offcut_resources(nodeid):
- """
-
- Obtain information about offcut resources on a node.
-
- nodeid
- The nodeid of the node whose offcut resources are to be queried.
-
- NodeCommunicationError
- If communication with the node failed, either because the node is down,
- the communication timed out, the signature was invalid, or the identity
- unauthorized for this action.
-
- None
-
- A string containing information about the node's offcut resources.
- """
- # TODO: This function might be more useful if it processed the string
- # returned by the nodemanager and return it from this function as some
- # well-defined data structure.
- return _do_public_node_request(nodeid, "GetOffcutResources")
-
-
-
-
-
-def get_vessel_resources(vesselhandle):
- """
-
- Obtain vessel resource/restrictions information.
-
- vesselhandle
- The vesselhandle of the vessels whose restrictions/resources info are to
- be returned.
-
- NodeCommunicationError
- If communication with the node failed, either because the node is down,
- the communication timed out, the signature was invalid, or the identity
- unauthorized for this action.
-
- None
-
- A string containing the vessel resource/restrictions information.
- """
- # TODO: This function might be more useful if it processed the string
- # returned by the nodemanager and return it from this function as some
- # well-defined data structure.
- nodeid, vesselname = get_nodeid_and_vesselname(vesselhandle)
- return _do_public_node_request(nodeid, "GetVesselResources", vesselhandle)
-
-
-
-
-
-def get_vessel_log(vesselhandle, identity):
- """
-
- Read the vessel log.
-
- vesselhandle
- The vesselhandle of the vessel whose log is to be read.
- identity
- The identity of either the owner or a user of the vessel.
-
- NodeCommunicationError
- If communication with the node failed, either because the node is down,
- the communication timed out, the signature was invalid, or the identity
- unauthorized for this action.
-
- None
-
- A string containing the data in the vessel log.
- """
- _validate_vesselhandle(vesselhandle)
- return _do_signed_vessel_request(identity, vesselhandle, "ReadVesselLog")
-
-
-
-
-
-def get_vessel_file_list(vesselhandle, identity):
- """
-
- Get a list of files that are on the vessel.
-
- vesselhandle
- The vesselhandle of the vessel whose file list is to be obtained.
- identity
- The identity of either the owner or a user of the vessel.
-
- NodeCommunicationError
- If communication with the node failed, either because the node is down,
- the communication timed out, the signature was invalid, or the identity
- unauthorized for this action.
-
- None
-
- A list of filenames (strings).
- """
- _validate_vesselhandle(vesselhandle)
- file_list_string = _do_signed_vessel_request(identity, vesselhandle, "ListFilesInVessel")
- if not file_list_string:
- return []
- else:
- return file_list_string.split(' ')
-
-
-
-
-
-def upload_file_to_vessel(vesselhandle, identity, local_filename, remote_filename=None):
- """
-
- Upload a file to a vessel.
-
- vesselhandle
- The vesselhandle of the vessel that the file is to be uploaded to.
- identity
- The identity of either the owner or a user of the vessel.
- local_filename
- The name of the local file to be uploaded. That can include a directory
- path.
- remote_filename
- (optional) The filename to use when storing the file on the vessel. If
- not provided, this will be the same as the basename of local_filename.
- Note that the remote_filename is subject to filename restrictions imposed
- on all vessels.
- TODO: describe the filename restrictions.
-
- NodeCommunicationError
- If communication with the node failed, either because the node is down,
- the communication timed out, the signature was invalid, or the identity
- unauthorized for this action.
-
- The file has been uploaded to the vessel.
-
- None
- """
- _validate_vesselhandle(vesselhandle)
-
- if remote_filename is None:
- remote_filename = os.path.basename(local_filename)
-
- fileobj = open(local_filename, "r")
- filedata = fileobj.read()
- fileobj.close()
-
- _do_signed_vessel_request(identity, vesselhandle, "AddFileToVessel", remote_filename, filedata)
-
-
-
-
-
-def download_file_from_vessel(vesselhandle, identity, remote_filename, local_filename=None,
- add_location_suffix=False, return_file_contents=False):
- """
-
- Download a file from a vessel.
-
- vesselhandle
- The vesselhandle of the vessel that the file is to be downloaded from.
- identity
- The identity of either the owner or a user of the vessel.
- remote_filename
- The file to be downloaded.
- local_filename
- (optional) The filename to use when saving the downloaded file locally.
- This can include a directory path.
- add_location_suffix
- (optional) Whether the nodelocation and vesselname should be suffixed to
- the end of the local filename when saving the file.
- local_filename
- (optional) If True, the downloaded file will not be saved locally and
- instead will be returned as a string instead of the local filename.
-
- NodeCommunicationError
- If communication with the node failed, either because the node is down,
- the communication timed out, the signature was invalid, or the identity
- unauthorized for this action.
-
- The file has been downloaded and, if return_file_contents is False, it has
- been saved to the local filesystem.
-
- If return_file_contents is False:
- The full filename where this file was ultimately saved to. This will be in
- the current working directory unless local_filename_prefix included a path
- to a different directory.
- If return_file_contents is True:
- The contents of the remote file as a string.
- """
- _validate_vesselhandle(vesselhandle)
-
- if not return_file_contents:
- if local_filename is None:
- local_filename = remote_filename
- if add_location_suffix:
- nodeid, vesselname = vesselhandle.split(':')
- nodelocation = get_node_location(nodeid)
- suffix = "_".join(nodelocation.split(':') + [vesselname])
- local_filename += "_" + suffix
-
- retrieveddata = _do_signed_vessel_request(identity, vesselhandle, "RetrieveFileFromVessel", remote_filename)
-
- if return_file_contents:
- return retrieveddata
- else:
- fileobj = open(local_filename, "w")
- fileobj.write(retrieveddata)
- fileobj.close()
- return local_filename
-
-
-
-
-
-def delete_file_in_vessel(vesselhandle, identity, filename):
- """
-
- Delete a file from a vessel.
-
- vesselhandle
- The vesselhandle of the vessel that the file is to be deleted from.
- identity
- The identity of either the owner or a user of the vessel.
- filename
- The name of the file to be deleted from the vessel.
-
- NodeCommunicationError
- If communication with the node failed, either because the node is down,
- the communication timed out, the signature was invalid, or the identity
- unauthorized for this action.
-
- The file has been deleted from the vessel.
-
- None
- """
- _validate_vesselhandle(vesselhandle)
- _do_signed_vessel_request(identity, vesselhandle, "DeleteFileInVessel", filename)
-
-
-
-
-
-def reset_vessel(vesselhandle, identity):
- """
-
- Stop the vessel if it is running and reset it to a fresh state. This will
- delete all files from the vessel.
-
- vesselhandle
- The vesselhandle of the vessel that is to be reset.
- identity
- The identity of either the owner or a user of the vessel.
-
- NodeCommunicationError
- If communication with the node failed, either because the node is down,
- the communication timed out, the signature was invalid, or the identity
- unauthorized for this action.
-
- The vessel has been reset. No program is running, no files exist on the
- vessel, and the vessel status is VESSEL_STATUS_FRESH.
-
- None
- """
- _validate_vesselhandle(vesselhandle)
- _do_signed_vessel_request(identity, vesselhandle, "ResetVessel")
-
-
-
-
-
-def start_vessel(vesselhandle, identity, program_file, arg_list=None):
- """
-
- Start a program running on a vessel.
-
- vesselhandle
- The vesselhandle of the vessel that is to be started.
- identity
- The identity of either the owner or a user of the vessel.
- program_file
- The name of the file that already exists on the vessel that is to be
- run on the vessel.
- arg_list
- (optional) A list of arguments to be passed to the program when it is
- started.
-
- NodeCommunicationError
- If communication with the node failed, either because the node is down,
- the communication timed out, the signature was invalid, or the identity
- unauthorized for this action.
-
- The vessel has been started, running the specified program.
-
- None
- """
- _validate_vesselhandle(vesselhandle)
- arg_string = program_file
- if arg_list is not None:
- arg_string += " " + " ".join(arg_list)
- _do_signed_vessel_request(identity, vesselhandle, "StartVessel", arg_string)
-
-
-
-
-
-
-def stop_vessel(vesselhandle, identity):
- """
-
- Stop the currently running program on a vessel, if there is one.
-
- vesselhandle
- The vesselhandle of the vessel that is to be stopped.
- identity
- The identity of either the owner or a user of the vessel.
-
- NodeCommunicationError
- If communication with the node failed, either because the node is down,
- the communication timed out, the signature was invalid, or the identity
- unauthorized for this action.
-
- If a program was running on the vessel, it has been stopped. The vessel
- state is either VESSEL_STATUS_STOPPED or VESSEL_STATUS_TERMINATED.
- TODO: verify this is the case and describe when these will happen.
-
- None
- """
- _validate_vesselhandle(vesselhandle)
- _do_signed_vessel_request(identity, vesselhandle, "StopVessel")
-
-
-
-
-
-def split_vessel(vesselhandle, identity, resourcedata):
- """
-
- Split a vessel into two new vessels.
-
- THIS OPERATION IS ONLY AVAILABLE TO THE OWNER OF THE VESSEL.
- If you have acquired the vessel through SeattleGENI, you are a user of the
- vessel, not an owner.
-
- vesselhandle
- The vesselhandle of the vessel that is to be split.
- identity
- The identity of the owner of the vessel.
- resourcedata
- The resourcedata that describes one of the vessels to be split from the
- original. The other vessel will have the remainder of the resources
- minus some overhead from the split.
-
- NodeCommunicationError
- If communication with the node failed, either because the node is down,
- the communication timed out, the signature was invalid, or the identity
- unauthorized for this action.
-
- The original vessel no longer exists (meaning that the vesselhandle passed
- in as an argument is no longer valid). The node instead has two new vessels.
-
- A tuple of the two new vesselhandles that resulted from the split. The
- first element of the tuple is the vesselhandle of the vessel that has the
- leftover resources from the split. The second element of the tuple is the
- vesselhandle of the vessel that has the exact resources specified in the
- resourcedata.
- """
- _validate_vesselhandle(vesselhandle)
- return _do_signed_vessel_request(identity, vesselhandle, "SplitVessel", resourcedata)
-
-
-
-
-
-def join_vessels(identity, vesselhandle1, vesselhandle2):
- """
-
- Join (combine) two vessels on the same node into one, larger vessel.
-
- THIS OPERATION IS ONLY AVAILABLE TO THE OWNER OF THE VESSEL.
- If you have acquired the vessel through SeattleGENI, you are a user of the
- vessel, not an owner.
-
- identity
- The identity of the owner of the vessel.
- vesselhandle1
- The vesselhandle of the one of the vessels to be comined.
- vesselhandle2
- The vesselhandle of the the other vessel to be combined.
-
- NodeCommunicationError
- If communication with the node failed, either because the node is down,
- the communication timed out, the signature was invalid, or the identity
- unauthorized for this action.
-
- Neither of the original two vessel exist (meaning that neither vesselhandle1
- nor vesselhandle2 are valid anymore). The node has one new vessel whose
- resources are the combination of the resource of the original two vessels
- plus some additional resources because of less overhead from less splits.
-
- The vesselhandle of the newly created vessel.
- """
- _validate_vesselhandle(vesselhandle1)
- _validate_vesselhandle(vesselhandle2)
- vesselname2 = vesselhandle2.split(":")[1]
- return _do_signed_vessel_request(identity, vesselhandle1, "JoinVessels", vesselname2)
-
-
-
-
-
-def set_vessel_owner(vesselhandle, identity, new_owner_identity):
- """
-
- Change the owner of a vessel.
-
- THIS OPERATION IS ONLY AVAILABLE TO THE OWNER OF THE VESSEL.
- If you have acquired the vessel through SeattleGENI, you are a user of the
- vessel, not an owner.
-
- vesselhandle
- The vesselhandle of the vessel whose owner is to be changed.
- identity
- The identity of the current owner of the vessel. This identity must have
- a private key.
- new_owner_identity
- The identity that the owner of the vessel is to be changed to. This
- identity only needs to have a public key.
-
- NodeCommunicationError
- If communication with the node failed, either because the node is down,
- the communication timed out, the signature was invalid, or the identity
- unauthorized for this action.
-
- The owner of the vessel has been changed.
-
- None
- """
- _validate_vesselhandle(vesselhandle)
- _do_signed_vessel_request(identity, vesselhandle, "ChangeOwner", new_owner_identity['publickey_str'])
-
-
-
-
-
-def set_vessel_advertise(vesselhandle, identity, advertise_enabled):
- """
-
- Set whether the vessel should be advertising or not.
-
- THIS OPERATION IS ONLY AVAILABLE TO THE OWNER OF THE VESSEL.
- If you have acquired the vessel through SeattleGENI, you are a user of the
- vessel, not an owner.
-