class TcpEndpointConfig(Config): exclusive = Config.boolean( label="One VM per connection", default=True, description= "Each Instance is used exclusively to serve a single connection.") disposable = Config.boolean(label="Kill on Disconnect", default=False, validate=lambda self: self.exclusive or not(self.disposable) or \ Config.error("Kill on Disconnect requires One VM per connection."), description="Discard backend instances on disconnect. Requires" \ + " 'One VM per connection'.") dispose_min = Config.integer(label="Minimum session time", default=30, validate=lambda self: self.dispose_min >= 0 or \ Config.error("Minimum session time must be non-negative."), description="Minimum session time (in seconds) before a session is" \ + " killed on disconnect.") reconnect = Config.integer(label="Reconnect Timeout", default=60, validate=lambda self: self.reconnect >= 0 or \ Config.error("The reconnect must be non-negative."), description="Amount of time a disconnected client has to reconnect before" \ + " the VM is returned to the pool.") client_subnets = Config.list( label="Client Subnets", order=7, description="Only allow connections from these client subnets.")
class HaproxyManagerConfig(Config): config_file = Config.string( label="Configuration path", default="/etc/haproxy/haproxy.cfg", description="The configuration file for HAProxy.") pid_file = Config.string(label="Pid file", default="/var/run/haproxy.pid", description="The HAProxy pid file.") stats_path = Config.string(label="Stats socket path", default="/var/run/haproxy.sock", description="The stats socket path.") stats_mode = Config.string(label="Stats socket mode", default="0666", description="The stats permission mode.") global_opts = Config.list(label="Other global configuration options", default=[ "log 127.0.0.1 local0", "log 127.0.0.1 local1 notice", "user haproxy", "group haproxy", ]) maxconn = Config.integer( label="Maximum connection", default=5000, description="Maximum number of simultaneous connections.") clitimeout = Config.integer(label="Client timeout", default=50000, description="Client connection timeout.")
class BaseOsEndpointConfig(Config): # Cached client. _client = None def __init__(self, *args, **kwargs): super(BaseOsEndpointConfig, self).__init__(*args, **kwargs) # Last refresh for the cache. self._last_refresh = None # Initialize our list cache. self._list_cache = [] # Initialize our floating IP cache. self._float_cache = {} # Common authentication elements. auth_url = Config.string( label="OpenStack Auth URL", default="http://localhost:5000/v2.0/", order=0, validate=lambda self: self.validate_connection_params(), alternates=["authurl"], description="The OpenStack authentication URL (OS_AUTH_URL).") username = Config.string( label="OpenStack User", default="admin", order=1, alternates=["user"], description="The user for authentication (OS_USERNAME).") password = Config.password( label="OpenStack Password", default="admin", order=2, alternates=["apikey"], description="The api key or password (OS_PASSWORD).") tenant_name = Config.string( "OpenStack Tenant/Project", default="admin", order=3, alternates=["project"], description="The project or tenant (OS_TENANT_NAME).") region_name = Config.string(label="Region Name", order=4, description="The region (OS_REGION_NAME).") list_rate_limit = Config.integer(label="Rate limit", default=120, order=1, validate=lambda self: self.list_rate_limit >= 0 or \ Config.error("Rate limit must be non-negative."), description="Limit list requests to this often.") # Elements common to launching and booting. security_groups = Config.list( label="Security Groups", order=5, description="Security groups for new instances.") availability_zone = Config.string( label="Availability Zone", order=5, description="Availability zone for new instances.") user_data = Config.text( "User Data", order=6, description="Script or cloud-config for new instances.") filter_instances = Config.boolean( "Filter Instances", default=False, order=7, description="Use only instances that match image, flavor, etc.") floating_ips = Config.list( label="Floating IPs", order=7, validate=lambda self: self.validate_floating_ips(), description="Floating IPs to distribute.") def novaclient(self): from novaclient import shell from novaclient.v1_1.client import Client as NovaClient if self._client is None: extensions = shell.OpenStackComputeShell()._discover_extensions( "1.1") self._client = NovaClient(self.username, self.password, self.tenant_name, self.auth_url, region_name=self.region_name, service_type="compute", extensions=extensions) return self._client def validate_connection_params(self, throwerror=True): import novaclient.exceptions try: self.novaclient().authenticate() except Exception, e: if throwerror: # If we got an unathorized exception, propagate. if isinstance(e, novaclient.exceptions.Unauthorized): Config.error(e.message) elif isinstance(e, novaclient.exceptions.EndpointNotFound): Config.error( "Problem connecting to cloud endpoint. Bad Region?") else: Config.error( "Could not connect to OpenStack cloud. Bad URL?") else: return False return True
class OsApiEndpointConfig(BaseOsEndpointConfig): instance_name = Config.string(label="Instance Name", order=2, validate=lambda self: self.instance_name or \ Config.error("Must provide an instance name."), description="The name given to new instances.") flavor_id = Config.string(label="Flavor", order=2, validate=lambda self: self.validate_flavor(), description="The flavor to use.") image_id = Config.string(label="Image", order=2, validate=lambda self: self.validate_image(), description="The image ID to boot.") key_name = Config.string(label="Key Name", order=2, validate=lambda self: self.validate_keyname(), description="The key_name (for injection).") network_conf = Config.list( label="Network Configuration", default=[], order=2, validate=lambda self: self.validate_network_conf(), description="Network configuration for scaled instances. " + "This is identical to the --nic parameter in novaclient. " + "One network configuration per line.") def validate_flavor(self): if self.validate_connection_params(False): avail = [ flavor._info['id'] for flavor in self.novaclient().flavors.list() ] if self.flavor_id in avail: return True else: Config.error("Flavor %s not found" % self.flavor_id) else: # Can't connect to cloud, ignore this param for now return True def validate_image(self): if self.validate_connection_params(False): avail = [ image._info['id'] for image in self.novaclient().images.list() ] if self.image_id in avail: return True else: Config.error("Image %s not found" % self.image_id) else: # Can't connect to cloud, ignore this param for now return True def validate_keyname(self): if self.key_name is None: # No keyname provided. return True elif self.validate_connection_params(False): avail = [ key._info['keypair']['name'] for key in self.novaclient().keypairs.list() ] if self.key_name in avail: return True else: Config.error("Keyname %s not found" % self.key_name) else: # Can't connect to cloud, ignore this param for now return True def validate_network_conf(self): if self.validate_connection_params(False): # Parse netspecs into objects. try: networks = OsApiEndpointConfig.parse_netspecs( self.network_conf) except Exception as ex: Config.error("Failed to parse network conf: %s" % str(ex)) known_networks = [ nw._info["id"] for nw in self.novaclient().networks.list() ] for network in networks: if (network["net-id"] is not None) and \ (network["net-id"] not in known_networks): Config.error("Network '%s' not found" % network) else: # Can't connect to cloud, ignore this param for now return True @staticmethod def parse_netspecs(netspecs): parsed_specs = [] for spec in netspecs: res = {"net-id": None, "v4-fixed-ip": None, "port-id": None} for keyval in spec.split(","): kv = keyval.split("=", 1) if len(kv) < 2: raise ValueError("No value given for key '%s'" % str(kv[0])) key = kv[0] value = kv[1] if key not in res: raise KeyError("'%s' is not a valid nic keyword" % str(key)) res[key] = value parsed_specs.append(res) return parsed_specs
class DockerEndpointConfig(Config): slots = Config.integer(label="Scheduler slots", default=10, order=0, validate=lambda self: self.slots >= 0 or \ Config.error("Slots must be greater than or equal to zero."), description="Slots required on a host in order to run.") image = Config.string(label="Image", default="", order=1, validate=lambda self: self.image or \ Config.error("No image provided."), description="The docker image to use.") command = Config.string(label="Command", default="", order=1, validate=lambda self: self.command or \ Config.error("No command provided."), description="The command to run inside the container.") user = Config.string(label="User", default="", order=2, description="The user used to run the command.") environment = Config.list(label="Environment", default=[], order=3, validate=lambda self: self.get_environment(), description="The environment for the command.") def get_environment(self): if self.environment: # Return as a dictionary of key=value pairs. return dict( map(lambda x: map(lambda y: y.strip(), x.split("=", 1)), self.environment)) else: return {} mem_limit = Config.integer(label="Memory limit", default=0, order=3, validate=lambda self: self.mem_limit >= 0 or \ Config.error("Memory limit must be non-negative."), description="The container memory limit.") dns = Config.string(label="DNS Sever", default="", order=4, description="The DNS server for the container.") hostname = Config.string(label="Hostname", default="", order=4, description="The hostname for the container.") def port(self): # Extract the port from the endpoint. from reactor.endpoint import EndpointConfig endpoint_config = EndpointConfig(obj=self) return endpoint_config.port