def vc_config(): username = '' while not username: username = prompt_response('Enter volttron central admin user name:') if not username: print('ERROR Invalid username') password = '' password2 = '' while not password: password = prompt_response('Enter volttron central admin password:'******'ERROR: Invalid password') continue password2 = prompt_response('Retype password:'******'t match") password = '' config = { 'users': { username: { 'password': hashlib.sha512(password).hexdigest(), 'groups': ['admin'] } } } return config
def vc_config(): username = '' while not username: username = prompt_response('Enter volttron central admin user name:') if not username: print('ERROR Invalid username') password = '' password2 = '' while not password: password = prompt_response('Enter volttron central admin password:'******'ERROR: Invalid password') continue password2 = prompt_response('Retype password:'******'t match") password = '' config = { 'users': { username: { 'password': hashlib.sha512(password).hexdigest(), 'groups': ['admin'] } } } return config
def prompt_upstream_servers(vhome): """ Prompt for upstream server configurations and save in rabbitmq_federation_config.yml :return: """ federation_config_file = os.path.join(vhome, 'rabbitmq_federation_config.yml') if os.path.exists(federation_config_file): federation_config = _read_config_file(federation_config_file) else: federation_config = {} upstream_servers = federation_config.get('federation-upstream', {}) prompt = 'Number of upstream servers to configure:' count = prompt_response(prompt, default=1) count = int(count) i = 0 for i in range(0, count): prompt = 'Hostname of the upstream server: ' host = prompt_response(prompt, mandatory=True) prompt = 'Port of the upstream server: ' port = prompt_response(prompt, default=5671) prompt = 'Virtual host of the upstream server: ' vhost = prompt_response(prompt, default='volttron') upstream_servers[host] = {'port': port, 'virtual-host': vhost} federation_config['federation-upstream'] = upstream_servers _write_to_config_file(federation_config_file, federation_config)
def process_rmq_inputs(args_dict, instance_name=None): #print(f"args_dict:{args_dict}, args") if not is_rabbitmq_available(): raise RuntimeError( "Rabbitmq Dependencies not installed please run python bootstrap.py --rabbitmq" ) confirm_volttron_home() vhome = get_home() if args_dict['config'] is not None: if not os.path.exists(vhome): os.makedirs(vhome, 0o755) if args_dict['installation-type'] == 'single': vhome_config = os.path.join(vhome, 'rabbitmq_config.yml') if args_dict['config'] != vhome_config: copy(args_dict['config'], vhome_config) elif args_dict['installation-type'] == 'federation': vhome_config = os.path.join(vhome, 'rabbitmq_federation_config.yml') if os.path.exists(vhome_config): prompt = f"rabbitmq_federation_config.yml already exists in VOLTTRON_HOME: {vhome}.\n" \ "Do you wish to use this config file? If no, rabbitmq_federation_config.yml \n" \ "will be replaced with new config file" prompt = prompt_response(prompt, valid_answers=y_or_n, default='N') if prompt in n: copy(args_dict['config'], vhome_config) else: r = copy(args_dict['config'], vhome_config) elif args_dict['installation-type'] == 'shovel': vhome_config = os.path.join(vhome, 'rabbitmq_shovel_config.yml') if os.path.exists(vhome_config): prompt = f"rabbitmq_shovel_config.yml already exists in VOLTTRON_HOME: {vhome}.\n" \ "Do you wish to use this config file? If no, rabbitmq_shovel_config.yml \n" \ "will be replaced with new config file" prompt = prompt_response(prompt, valid_answers=y_or_n, default='N') if prompt in n: copy(args_dict['config'], vhome_config) else: r = copy(args_dict['config'], vhome_config) else: print( "Invalid installation type. Acceptable values single|federation|shovel" ) sys.exit(1) setup_rabbitmq_volttron(args_dict['installation-type'], verbose, instance_name=instance_name, max_retries=args_dict['max_retries']) else: setup_rabbitmq_volttron(args_dict['installation-type'], verbose, prompt=True, instance_name=instance_name, max_retries=args_dict['max_retries'])
def do_vip(): global config_opts parsed = urlparse.urlparse( config_opts.get('vip-address', 'tcp://127.0.0.1:22916')) vip_address = None if parsed.hostname is not None and parsed.scheme is not None: vip_address = parsed.scheme + '://' + parsed.hostname vip_port = parsed.port else: vip_address = 'tcp://127.0.0.1' vip_port = 22916 available = False while not available: valid_address = False while not valid_address: if config_opts['message-bus'] == 'rmq': prompt = """ The rmq message bus has a backward compatibility layer with current zmq instances. What is the zmq bus's vip address?""" else: prompt = "What is the vip address?" new_vip_address = prompt_response(prompt, default=vip_address) valid_address = is_valid_url(new_vip_address, ['tcp']) if valid_address: vip_address = new_vip_address else: print("Address is not valid.") valid_port = False while not valid_port: prompt = 'What is the port for the vip address?' new_vip_port = prompt_response(prompt, default=vip_port) valid_port = is_valid_port(new_vip_port) if valid_port: vip_port = new_vip_port else: print("Port is not valid.") while vip_address.endswith('/'): vip_address = vip_address[:-1] attempted_address = '{}:{}'.format(vip_address, vip_port) if not _is_bound_already(attempted_address): available = True else: print('\nERROR: That address has already been bound to.') config_opts['vip-address'] = '{}:{}'.format(vip_address, vip_port)
def do_vcp(): global config_opts # Default instance name to the vip address. instance_name = config_opts.get('instance-name', config_opts.get('vip-address')) instance_name = instance_name.strip('"') valid_name = False while not valid_name: prompt = 'Enter the name of this instance.' new_instance_name = prompt_response(prompt, default=instance_name) if new_instance_name: valid_name = True instance_name = new_instance_name config_opts['instance-name'] = '"{}"'.format(instance_name) vc_address = config_opts.get('volttron-central-address', config_opts.get('bind-web-address', 'http://127.0.0.1')) parsed = urlparse.urlparse(vc_address) address_only = vc_address port_only = None if parsed.port is not None: address_only = parsed.scheme + '://' + parsed.hostname port_only = parsed.port else: port_only = 8080 valid_vc = False while not valid_vc: prompt = "Enter volttron central's web address" new_vc_address = prompt_response(prompt, default=address_only) valid_vc = is_valid_url(new_vc_address, ['http', 'https']) if valid_vc: vc_address = new_vc_address vc_port = None while True: prompt = 'What is the port for volttron central?' new_vc_port = prompt_response(prompt, default=port_only) if is_valid_port(new_vc_port): vc_port = new_vc_port break new_address = '{}:{}'.format(vc_address, vc_port) config_opts['volttron-central-address'] = new_address return {}
def do_web_enabled_zmq(vhome): global config_opts # Full implies that it will have a port on it as well. Though if it's # not in the address that means that we haven't set it up before. full_bind_web_address = config_opts.get('bind-web-address', 'https://' + get_hostname()) parsed = urlparse(full_bind_web_address) address_only = full_bind_web_address port_only = None if parsed.port is not None: address_only = parsed.scheme + '://' + parsed.hostname port_only = parsed.port else: port_only = 8443 valid_address = False external_ip = None while not valid_address: prompt = 'What is the protocol for this instance?' new_scheme = prompt_response(prompt, default=parsed.scheme) new_external_ip = new_scheme + '://' + parsed.hostname valid_address = is_valid_url(new_external_ip, ['http', 'https']) if valid_address: external_ip = new_external_ip print("Web address set to: {}".format(external_ip)) valid_port = False vc_port = None while not valid_port: prompt = 'What is the port for this instance?' if new_scheme == 'http' and port_only == 8443: port_only = 8080 new_vc_port = prompt_response(prompt, default=port_only) valid_port = is_valid_port(new_vc_port) if valid_port: vc_port = new_vc_port while external_ip.endswith("/"): external_ip = external_ip[:-1] parsed = urlparse(external_ip) config_opts['bind-web-address'] = '{}:{}'.format(external_ip, vc_port) if config_opts['message-bus'] == 'zmq' and parsed.scheme == "https": get_cert_and_key(vhome)
def do_vcp(): global config_opts # Default instance name to the vip address. instance_name = config_opts.get('instance-name', config_opts.get('vip-address')) instance_name = instance_name.strip('"') valid_name = False while not valid_name: prompt = 'Enter the name of this instance.' new_instance_name = prompt_response(prompt, default=instance_name) if new_instance_name: valid_name = True instance_name = new_instance_name config_opts['instance-name'] = '"{}"'.format(instance_name) vc_address = config_opts.get( 'volttron-central-address', config_opts.get('bind-web-address', 'http://127.0.0.1')) parsed = urlparse.urlparse(vc_address) address_only = vc_address port_only = None if parsed.port is not None: address_only = parsed.scheme + '://' + parsed.hostname port_only = parsed.port else: port_only = 8080 valid_vc = False while not valid_vc: prompt = "Enter volttron central's web address" new_vc_address = prompt_response(prompt, default=address_only) valid_vc = is_valid_url(new_vc_address, ['http', 'https']) if valid_vc: vc_address = new_vc_address vc_port = None while True: prompt = 'What is the port for volttron central?' new_vc_port = prompt_response(prompt, default=port_only) if is_valid_port(new_vc_port): vc_port = new_vc_port break new_address = '{}:{}'.format(vc_address, vc_port) config_opts['volttron-central-address'] = new_address return {}
def do_vc(): global config_opts # Full implies that it will have a port on it as well. Though if it's # not in the address that means that we haven't set it up before. full_bind_web_address = config_opts.get('bind-web-address', 'http://127.0.0.1') parsed = urlparse.urlparse(full_bind_web_address) address_only = full_bind_web_address port_only = None if parsed.port is not None: address_only = parsed.scheme + '://' + parsed.hostname port_only = parsed.port else: port_only = 8080 print(""" In order for external clients to connect to volttron central or the instance itself, the instance must bind to a tcp address. If testing this can be an internal address such as 127.0.0.1. """) valid_address = False external_ip = None while not valid_address: prompt = 'Please enter the external ipv4 address for this instance? ' new_external_ip = prompt_response(prompt, default=address_only) valid_address = is_valid_url(new_external_ip, ['http', 'https']) if valid_address: external_ip = new_external_ip valid_port = False vc_port = None while not valid_port: prompt = 'What is the port for volttron central?' new_vc_port = prompt_response(prompt, default=port_only) valid_port = is_valid_port(new_vc_port) if valid_port: vc_port = new_vc_port while external_ip.endswith("/"): external_ip = external_ip[:-1] config_opts['bind-web-address'] = '{}:{}'.format(external_ip, vc_port) resp = vc_config() print('Installing volttron central') return resp
def do_vc(): global config_opts # Full implies that it will have a port on it as well. Though if it's # not in the address that means that we haven't set it up before. full_bind_web_address = config_opts.get('bind-web-address', 'http://127.0.0.1') parsed = urlparse.urlparse(full_bind_web_address) address_only = full_bind_web_address port_only = None if parsed.port is not None: address_only = parsed.scheme + '://' + parsed.hostname port_only = parsed.port else: port_only = 8080 print(""" In order for external clients to connect to volttron central or the instance itself, the instance must bind to a tcp address. If testing this can be an internal address such as 127.0.0.1. """) valid_address = False external_ip = None while not valid_address: prompt = 'Please enter the external ipv4 address for this instance? ' new_external_ip = prompt_response(prompt, default=address_only) valid_address = is_valid_url(new_external_ip, ['http', 'https']) if valid_address: external_ip = new_external_ip valid_port = False vc_port = None while not valid_port: prompt = 'What is the port for volttron central?' new_vc_port = prompt_response(prompt, default=port_only) valid_port = is_valid_port(new_vc_port) if valid_port: vc_port = new_vc_port while external_ip.endswith("/"): external_ip = external_ip[:-1] config_opts['bind-web-address'] = '{}:{}'.format(external_ip, vc_port) resp = vc_config() print('Installing volttron central') return resp
def func(*args, **kwargs): print('Configuring {}.'.format(agent_dir)) config = config_func(*args, **kwargs) _update_config_file() #TODO: Optimize long vcfg install times #TODO: (potentially only starting the platform once per vcfg) _start_platform() _install_agent(agent_dir, config, tag, identity) if not _is_agent_installed(tag): print(tag + ' not installed correctly!') _shutdown_platform() return if post_install_func: post_install_func() autostart = prompt_response('Should the agent autostart?', valid_answers=y_or_n, default='N') if autostart in y: _cmd(['volttron-ctl', 'enable', '--tag', tag]) _shutdown_platform() if identity is not None: # If identity was specified then we don't want the global # AGENT_VIP_IDENTITY to be set. os.environ.pop('AGENT_VIP_IDENTITY', None)
def get_platform_instance_name(vhome=None, prompt=False): platform_config = load_platform_config(vhome) instance_name = platform_config.get('instance-name', None) if instance_name is not None: instance_name = instance_name.strip('"') if prompt: if not instance_name: instance_name = 'volttron1' instance_name = prompt_response("Name of this volttron instance:", mandatory=True, default=instance_name) else: if not instance_name: _log.warning("Using hostname as instance name.") if os.path.isfile('/etc/hostname'): with open('/etc/hostname') as f: instance_name = f.read().strip() bus = platform_config.get('message-bus') if bus is None: bus = get_messagebus() store_message_bus_config(bus, instance_name) else: err = "No instance-name is configured in $VOLTTRON_HOME/config. Please set instance-name in " \ "$VOLTTRON_HOME/config" _log.error(err) raise KeyError(err) return instance_name
def do_message_bus(): global config_opts bus_type = None valid_bus = False while not valid_bus: try: rmq_config = RMQConfig() prompt = 'What type of message bus (rmq/zmq)?' new_bus = prompt_response(prompt, default='zmq') valid_bus = is_valid_bus(new_bus) if valid_bus: bus_type = new_bus else: print( "Message type is not valid. Valid entries are zmq or rmq.") except AssertionError: new_bus = 'zmq' valid_bus = is_valid_bus(new_bus) if valid_bus: bus_type = new_bus print("Message bus set to zmq") if bus_type == 'rmq': check_rmq_setup() config_opts['message-bus'] = bus_type
def _prompt_ssl(): prompt = prompt_response('\nEnable SSL Authentication:', valid_answers=y_or_n, default='Y') if prompt in y: return True else: return False
def do_vip(): global config_opts parsed = urlparse.urlparse( config_opts.get('vip-address', 'tcp://127.0.0.1:22916')) vip_address = None if parsed.hostname is not None and parsed.scheme is not None: vip_address = parsed.scheme + '://' + parsed.hostname vip_port = parsed.port else: vip_address = 'tcp://127.0.0.1' vip_port = 22916 available = False while not available: valid_address = False while not valid_address: prompt = 'What is the external instance ipv4 address?' new_vip_address = prompt_response(prompt, default=vip_address) valid_address = is_valid_url(new_vip_address, ['tcp']) if valid_address: vip_address = new_vip_address else: print("Address is not valid") valid_port = False while not valid_port: prompt = 'What is the instance port for the vip address?' new_vip_port = prompt_response(prompt, default=vip_port) valid_port = is_valid_port(new_vip_port) if valid_port: vip_port = new_vip_port else: print("Port is not valid") while vip_address.endswith('/'): vip_address = vip_address[:-1] attempted_address = '{}:{}'.format(vip_address, vip_port) if not _is_bound_already(attempted_address): available = True else: print('\nERROR: That address has already been bound to.') config_opts['vip-address'] = '{}:{}'.format(vip_address, vip_port)
def do_vip(): global config_opts parsed = urlparse.urlparse(config_opts.get('vip-address', 'tcp://127.0.0.1:22916')) vip_address = None if parsed.hostname is not None and parsed.scheme is not None: vip_address = parsed.scheme + '://' + parsed.hostname vip_port = parsed.port else: vip_address = 'tcp://127.0.0.1' vip_port = 22916 available = False while not available: valid_address = False while not valid_address: prompt = 'What is the external instance ipv4 address?' new_vip_address = prompt_response(prompt, default=vip_address) valid_address = is_valid_url(new_vip_address, ['tcp']) if valid_address: vip_address = new_vip_address else: print("Address is not valid") valid_port = False while not valid_port: prompt = 'What is the instance port for the vip address?' new_vip_port = prompt_response(prompt, default=vip_port) valid_port = is_valid_port(new_vip_port) if valid_port: vip_port = new_vip_port else: print("Port is not valid") while vip_address.endswith('/'): vip_address = vip_address[:-1] attempted_address = '{}:{}'.format(vip_address, vip_port) if not _is_bound_already(attempted_address): available = True else: print('\nERROR: That address has already been bound to.') config_opts['vip-address'] = '{}:{}'.format(vip_address, vip_port)
def add_fake_device_to_configstore(): prompt = 'Install a fake device on the master driver?' response = prompt_response(prompt, valid_answers=y_or_n, default='N') if response in y: _cmd(['volttron-ctl', 'config', 'store', PLATFORM_DRIVER, 'fake.csv', 'examples/configurations/drivers/fake.csv', '--csv']) _cmd(['volttron-ctl', 'config', 'store', PLATFORM_DRIVER, 'devices/fake-campus/fake-building/fake-device', 'examples/configurations/drivers/fake.config'])
def prompt_port(default_port, prompt): valid_port = False while not valid_port: port = prompt_response(prompt, default=default_port) try: port = int(port) return port except ValueError: _log.error("Invalid port. Port should be an integer")
def _get_agent_metadata(silent): results = { "version": "0.1", "author": None, "author_email": None, "url": None, "description": None } if silent: return results results["version"] = prompt_response("Agent version number:", default="0.1") results["author"] = prompt_response("Agent author:", default="") results["author_email"] = prompt_response("Author's email address:", default="") results["url"] = prompt_response("Agent homepage:", default="") results["description"] = prompt_response("Short description of the agent:", default="") return results
def _get_agent_metadata(silent): results = { "version": "0.1", "author": None, "author_email": None, "url": None, "description": None } if silent: return results results["version"] = prompt_response("Agent version number:", default="0.1") results["author"] = prompt_response("Agent author:", default="") results["author_email"] = prompt_response("Author's email address:", default="") results["url"] = prompt_response("Agent homepage:", default="") results["description"] = prompt_response("Short description of the agent:", default="") return results
def confirm_volttron_home(): global prompt_vhome volttron_home = get_home() if prompt_vhome: print( '\nYour VOLTTRON_HOME currently set to: {}'.format(volttron_home)) prompt = '\nIs this the volttron you are attempting to setup?' if not prompt_response(prompt, valid_answers=y_or_n, default='Y') in y: print( '\nPlease execute with VOLTRON_HOME=/your/path volttron-cfg to ' 'modify VOLTTRON_HOME.\n') sys.exit(1)
def add_fake_device_to_configstore(): prompt = 'Install a fake device on the master driver?' response = prompt_response(prompt, valid_answers=y_or_n, default='N') if response in y: _cmd([ 'volttron-ctl', 'config', 'store', PLATFORM_DRIVER, 'fake.csv', 'examples/configurations/drivers/fake.csv', '--csv' ]) _cmd([ 'volttron-ctl', 'config', 'store', PLATFORM_DRIVER, 'devices/fake-campus/fake-building/fake-device', 'examples/configurations/drivers/fake.config' ])
def _prompt_rmq_home(rabbitmq_server): default_dir = os.path.join(os.path.expanduser("~"), "rabbitmq_server", rabbitmq_server) valid_dir = False while not valid_dir: prompt = 'RabbitMQ server home:' rmq_home = prompt_response(prompt, default=default_dir) if os.path.exists(rmq_home) and \ os.path.exists(os.path.join(rmq_home, 'sbin/rabbitmq-server')): return rmq_home else: _log.error("Invalid install directory. Unable to find {} ".format( os.path.join(rmq_home, 'sbin/rabbitmq-server'))) return None
def _create_web_certs(): global config_opts """ Utility to create web server certificates Designed to be used in conjecture with get_cert_and_key As such, it assumes that the program has already checked for existing certs, and prompted the user to enter in certificates that they have generated separately. """ crts = certs.Certs() try: crts.ca_cert() except certs.CertError: print("WARNING! CA certificate does not exist.") prompt_str = "Create new root CA?" prompt = prompt_response(prompt_str, valid_answers=y_or_n, default='Y') if prompt in y: cert_data = {} print( "\nPlease enter the following details for web server certificate:" ) prompt = '\tCountry:' cert_data['country'] = prompt_response(prompt, default='US') prompt = '\tState:' cert_data['state'] = prompt_response(prompt, mandatory=True) prompt = '\tLocation:' cert_data['location'] = prompt_response(prompt, mandatory=True) prompt = '\tOrganization:' cert_data['organization'] = prompt_response(prompt, mandatory=True) prompt = '\tOrganization Unit:' cert_data['organization-unit'] = prompt_response(prompt, mandatory=True) cert_data['common-name'] = get_platform_instance_name( ) + '-root-ca' data = { 'C': cert_data.get('country'), 'ST': cert_data.get('state'), 'L': cert_data.get('location'), 'O': cert_data.get('organization'), 'OU': cert_data.get('organization-unit'), 'CN': cert_data.get('common-name') } crts.create_root_ca(overwrite=False, **data) copy(crts.cert_file(crts.root_ca_name), crts.cert_file(crts.trusted_ca_name)) else: return 1 print("Creating new web server certificate.") crts.create_signed_cert_files(name=PLATFORM_WEB + "-server", cert_type='server', ca_name=crts.root_ca_name, fqdn=get_hostname()) return 0
def wizard(): global config_opts """Routine for configuring an installed volttron instance. The function interactively sets up the instance for working with volttron central and the discovery service. """ # Start true configuration here. volttron_home = get_home() confirm_volttron_home() _load_config() _update_config_file() do_message_bus() do_vip() _update_config_file() prompt = 'Is this instance web enabled?' response = prompt_response(prompt, valid_answers=y_or_n, default='N') if response in y: if config_opts['message-bus'] == 'rmq': do_web_enabled_rmq(volttron_home) elif config_opts['message-bus'] == 'zmq': do_web_enabled_zmq(volttron_home) _update_config_file() prompt = 'Is this an instance of volttron central?' response = prompt_response(prompt, valid_answers=y_or_n, default='N') if response in y: do_vc() prompt = 'Will this instance be controlled by volttron central?' response = prompt_response(prompt, valid_answers=y_or_n, default='Y') if response in y: do_vcp() prompt = 'Would you like to install a platform historian?' response = prompt_response(prompt, valid_answers=y_or_n, default='N') if response in y: do_platform_historian() prompt = 'Would you like to install a master driver?' response = prompt_response(prompt, valid_answers=y_or_n, default='N') if response in y: do_master_driver() prompt = 'Would you like to install a listener agent?' response = prompt_response(prompt, valid_answers=y_or_n, default='N') if response in y: do_listener() print('Finished configuration!\n') print('You can now start the volttron instance.\n') print('If you need to change the instance configuration you can edit') print('the config file is at {}/config\n'.format(volttron_home))
def wizard(): """Routine for configuring an insalled volttron instance. The function interactively sets up the instance for working with volttron central and the discovery service. """ # Start true configuration here. volttron_home = get_home() print('\nYour VOLTTRON_HOME currently set to: {}'.format(volttron_home)) prompt = '\nIs this the volttron you are attempting to setup? ' if not prompt_response(prompt, valid_answers=y_or_n, default='Y') in y: print( '\nPlease execute with VOLTRON_HOME=/your/path volttron-cfg to ' 'modify VOLTTRON_HOME.\n') return _load_config() do_vip() _install_config_file() prompt = 'Is this instance a volttron central?' response = prompt_response(prompt, valid_answers=y_or_n, default='N') if response in y: do_vc() prompt = 'Will this instance be controlled by volttron central?' response = prompt_response(prompt, valid_answers=y_or_n, default='Y') if response in y: do_vcp() prompt = 'Would you like to install a platform historian?' response = prompt_response(prompt, valid_answers=y_or_n, default='N') if response in y: do_platform_historian() prompt = 'Would you like to install a master driver?' response = prompt_response(prompt, valid_answers=y_or_n, default='N') if response in y: do_master_driver() prompt = 'Would you like to install a listener agent?' response = prompt_response(prompt, valid_answers=y_or_n, default='N') if response in y: do_listener() print('Finished configuration\n') print('You can now start the volttron instance.\n') print('If you need to change the instance configuration you can edit') print('the config file at {}/config\n'.format(volttron_home))
def wizard(): """Routine for configuring an insalled volttron instance. The function interactively sets up the instance for working with volttron central and the discovery service. """ # Start true configuration here. volttron_home = get_home() print('\nYour VOLTTRON_HOME currently set to: {}'.format(volttron_home)) prompt = '\nIs this the volttron you are attempting to setup? ' if not prompt_response(prompt, valid_answers=y_or_n, default='Y') in y: print( '\nPlease execute with VOLTRON_HOME=/your/path volttron-cfg to ' 'modify VOLTTRON_HOME.\n') return _load_config() do_vip() _install_config_file() prompt = 'Is this instance a volttron central?' response = prompt_response(prompt, valid_answers=y_or_n, default='N') if response in y: do_vc() prompt = 'Will this instance be controlled by volttron central?' response = prompt_response(prompt, valid_answers=y_or_n, default='Y') if response in y: do_vcp() prompt = 'Would you like to install a platform historian?' response = prompt_response(prompt, valid_answers=y_or_n, default='N') if response in y: do_platform_historian() prompt = 'Would you like to install a master driver?' response = prompt_response(prompt, valid_answers=y_or_n, default='N') if response in y: do_master_driver() prompt = 'Would you like to install a listener agent?' response = prompt_response(prompt, valid_answers=y_or_n, default='N') if response in y: do_listener() print('Finished configuration\n') print('You can now start the volttron instance.\n') print('If you need to change the instance configuration you can edit') print('the config file at {}/config\n'.format(volttron_home))
def do_instance_name(): """ Prompts the user for volttron instance-name. "volttron1" will be used as the default otherwise. """ # TODO: Set constraints on what can be used for volttron instance-name. global config_opts instance_name = config_opts.get('instance-name', 'volttron1') instance_name = instance_name.strip('"') valid_name = False while not valid_name: prompt = 'What is the name of this instance?' new_instance_name = prompt_response(prompt, default=instance_name) if new_instance_name: valid_name = True instance_name = new_instance_name config_opts['instance-name'] = '"{}"'.format(instance_name)
def func(*args, **kwargs): if identity is not None: os.environ['AGENT_VIP_IDENTITY'] = identity print 'Configuring {}'.format(agent_dir) config = config_func(*args, **kwargs) _install_config_file() _start_platform() _install_agent(agent_dir, config, tag) if post_install_func: post_install_func() autostart = prompt_response('Should agent autostart?', valid_answers=y_or_n, default='N') if autostart in y: _cmd(['volttron-ctl', 'enable', '--tag', tag]) _shutdown_platform() if identity is not None: os.environ.pop('AGENT_VIP_IDENTITY')
def func(*args, **kwargs): if identity is not None: os.environ['AGENT_VIP_IDENTITY'] = identity print 'Configuring {}'.format(agent_dir) config = config_func(*args, **kwargs) _install_config_file() _start_platform() _install_agent(agent_dir, config, tag) if post_install_func: post_install_func() autostart = prompt_response('Should agent autostart?', valid_answers=y_or_n, default='N') if autostart in y: _cmd(['volttron-ctl', 'enable', '--tag', tag]) _shutdown_platform() if identity is not None: os.environ.pop('AGENT_VIP_IDENTITY')
def do_message_bus(): global config_opts bus_type = None valid_bus = False while not valid_bus: prompt = 'What type of message bus (rmq/zmq)?' new_bus = prompt_response(prompt, default='zmq') valid_bus = is_valid_bus(new_bus) if valid_bus: bus_type = new_bus else: print("Message type is not valid. Valid entries are zmq or rmq.") if bus_type == 'rmq': if not is_rabbitmq_available(): print("RabbitMQ has not been set up!") print( "Please run scripts/rabbit_dependencies.sh and bootstrap --rabbitmq before running vcfg." ) sys.exit() # print("Setting up now...") # set_dependencies_rmq() # print("Done!") # if not _check_dependencies_met('rabbitmq'): # print("Rabbitmq dependencies not installed. Installing now...") # set_dependencies("rabbitmq") # print("Done!") try: check_rmq_setup() except AssertionError: print( "RabbitMQ setup is incomplete. RabbitMQ server directory is missing." ) print("Please run bootstrap --rabbitmq before running vcfg") sys.exit() config_opts['message-bus'] = bus_type
def do_vcp(): global config_opts is_vc = False vctl_list_process = Popen(['vctl', 'list'], env=os.environ, stdout=subprocess.PIPE, stderr=subprocess.PIPE) vctl_list = vctl_list_process.communicate() vctl_list_output = ''.join([v.decode('utf-8') for v in vctl_list]) try: vc_address = config_opts['volttron-central-address'] no_vc_address = False except KeyError: no_vc_address = True try: if no_vc_address: vc_address = config_opts['bind-web-address'] if VOLTTRON_CENTRAL in vctl_list_output: is_vc = True except KeyError: vc_address = config_opts.get( 'volttron-central-address', config_opts.get('bind-web-address', 'https://' + get_hostname())) if not is_vc: parsed = urlparse(vc_address) address_only = vc_address port_only = None if parsed.port is not None: address_only = parsed.scheme + '://' + parsed.hostname port_only = parsed.port else: port_only = 8443 valid_vc = False while not valid_vc: prompt = "What is the hostname for volttron central?" new_vc_address = prompt_response(prompt, default=address_only) valid_vc = is_valid_url(new_vc_address, ['http', 'https']) if valid_vc: vc_address = new_vc_address vc_port = None while True: prompt = 'What is the port for volttron central?' new_vc_port = prompt_response(prompt, default=port_only) if is_valid_port(new_vc_port): vc_port = new_vc_port break new_address = '{}:{}'.format(vc_address, vc_port) else: new_address = vc_address print('Volttron central address set to {}'.format(new_address)) config_opts['volttron-central-address'] = new_address return {}
def _create_certs(rmq_config, admin_client_name, server_cert_name): """ Utility method to create certificates :param client_cert_name: client (agent) cert name :param instance_ca_name: VOLTTRON instance name :param server_cert_name: RabbitMQ sever name :return: """ crts = rmq_config.crts if rmq_config.crts.ca_exists(): if rmq_config.use_existing_certs is None: attributes = crts.get_cert_subject(crts.root_ca_name) prompt_str = "Found {} with the following attributes. \n {} \n Do " \ "you want to use this certificate: ".format( crts.cert_file(crts.root_ca_name), attributes) prompt = prompt_response(prompt_str, valid_answers=y_or_n, default='Y') if prompt in y: return elif rmq_config.use_existing_certs: return if rmq_config.use_existing_certs is None: prompt_str = "\n**IMPORTANT:**\nCreating a new Root CA will " \ "invalidate " \ "any existing agent certificate and hence any existing " \ "certificates will be deleted. If you have federation " \ "or shovel setup, you will have to share the new " \ "certificate with the other volttron instance(s) for " \ "the shovel/federation connections to work. " \ "Do you want to create a new Root CA." prompt = prompt_response(prompt_str, valid_answers=y_or_n, default='N') if prompt not in y: return # We are creating new CA cert so delete any existing certs. The user has # already been warned for d in [crts.cert_dir, crts.private_dir, crts.ca_db_dir]: for x in os.listdir(d): os.remove(os.path.join(d, x)) _log.info('\n Creating root ca for volttron instance: {}'.format( crts.cert_file(crts.root_ca_name))) cert_data = rmq_config.certificate_data if not cert_data or \ not (all(k in cert_data for k in ['country', 'state', 'location', 'organization', 'organization-unit', 'common-name']) or all( k in cert_data for k in ['ca-public-key', 'ca-private-key'])): print( "\nERROR:\n" "No certificate data found in {} or certificate data is " "incomplete. certificate-data should either contain all " "the details necessary to create a self signed CA or " "point to the file path of an existing CA's public and " "private key. Please refer to example " "config at examples/configurations/rabbitmq/rabbitmq_config.yml" " to see list of ssl certificate data to be configured".format( rmq_config.volttron_rmq_config)) exit(1) if cert_data.get('ca-public-key'): # using existing CA copy(cert_data['ca-public-key'], rmq_config.crts.cert_file(crts.root_ca_name)) copy(cert_data['ca-private-key'], rmq_config.crts.private_key_file(crts.root_ca_name)) else: data = {'C': cert_data.get('country'), 'ST': cert_data.get('state'), 'L': cert_data.get('location'), 'O': cert_data.get('organization'), 'OU': cert_data.get('organization-unit'), 'CN': cert_data.get('common-name')} _log.info("Creating root ca with the following info: {}".format(data)) crts.create_root_ca(overwrite=False, **data) # create a copy of the root ca as instance_name-trusted-cas.crt. copy(rmq_config.crts.cert_file(crts.root_ca_name), rmq_config.crts.cert_file(crts.trusted_ca_name)) crts.create_signed_cert_files(server_cert_name, cert_type='server', fqdn=rmq_config.hostname) crts.create_signed_cert_files(admin_client_name, cert_type='client')
def setup_rabbitmq_volttron(setup_type, verbose=False, prompt=False, instance_name=None, rmq_conf_file=None, env=None): """ Setup VOLTTRON instance to run with RabbitMQ message bus. :param setup_type: single - Setup to run as single instance federation - Setup to connect multiple VOLTTRON instances as a federation shovel - Setup shovels to forward local messages to remote instances :param verbose :param prompt :raises RabbitMQSetupAlreadyError """ if not instance_name: instance_name = get_platform_instance_name(prompt=True) # Store config this is checked at startup store_message_bus_config(message_bus='rmq', instance_name=instance_name) rmq_config = RMQConfig() if verbose: _log.setLevel(logging.DEBUG) _log.debug("verbose set to True") _log.debug(get_home()) logging.getLogger("requests.packages.urllib3.connectionpool" "").setLevel(logging.DEBUG) else: _log.setLevel(logging.INFO) logging.getLogger("requests.packages.urllib3.connectionpool" "").setLevel(logging.WARN) if prompt: # ignore any existing rabbitmq_config.yml in vhome. Prompt user and # generate a new rabbitmq_config.yml _create_rabbitmq_config(rmq_config, setup_type) # Load either the newly created config or config passed try: rmq_config.load_rmq_config() except (yaml.parser.ParserError, yaml.scanner.ScannerError, yaml.YAMLError) as exc: _log.error("Error: YAML file cannot parsed properly. Check the contents of the file") return exc except IOError as exc: _log.error("Error opening {}. Please create a rabbitmq_config.yml " "file in your volttron home. If you want to point to a " "volttron home other than {} please set it as the " "environment variable VOLTTRON_HOME".format( rmq_config.volttron_rmq_config, rmq_config.volttron_home)) _log.error("\nFor single setup, configuration file must at least " "contain host and ssl certificate details. For federation " "and shovel setup, config should contain details about the " "volttron instance with which communication needs " "to be established. Please refer to example config file " "at examples/configurations/rabbitmq/rabbitmq_config.yml") raise if not rmq_conf_file: rmq_conf_file = os.path.join(rmq_config.rmq_home, "etc/rabbitmq/rabbitmq.conf") invalid = True if setup_type in ["all", "single"]: invalid = False # Verify that the rmq_conf_file if exists is removed before continuing. message = f"A rabbitmq conf file {rmq_conf_file} already exists.\n" \ "In order for setup to proceed it must be removed.\n" if os.path.exists(rmq_conf_file): print(message) while os.path.exists(rmq_conf_file): value = prompt_response(f"Remove {rmq_conf_file}? ", y_or_n) if value in y: os.remove(rmq_conf_file) _start_rabbitmq_without_ssl(rmq_config, rmq_conf_file, env=env) _log.debug("Creating rabbitmq virtual hosts and required users for " "volttron") # Create local RabbitMQ setup - vhost, exchange etc. # should be called after _start_rabbitmq_without_ssl rmq_mgmt = RabbitMQMgmt() success = rmq_mgmt.init_rabbitmq_setup() if success and rmq_config.is_ssl: _setup_for_ssl_auth(rmq_config, rmq_conf_file, env=env) # Create utility scripts script_path = os.path.dirname(os.path.realpath(__file__)) src_home = os.path.dirname(os.path.dirname(script_path)) start_script = os.path.join(src_home, 'start-rabbitmq') with open(start_script, 'w+') as f: f.write(os.path.join(rmq_config.rmq_home, 'sbin', 'rabbitmq-server') + ' -detached') f.write(os.linesep) f.write("sleep 5") # give a few seconds for all plugins to be ready os.chmod(start_script, 0o755) stop_script = os.path.join(src_home, 'stop-rabbitmq') with open(stop_script, 'w+') as f: f.write(os.path.join(rmq_config.rmq_home, 'sbin', 'rabbitmqctl') + ' stop') os.chmod(stop_script, 0o755) # symlink to rmq log log_name = os.path.join(src_home, 'rabbitmq.log') if os.path.lexists(log_name): os.unlink(log_name) os.symlink(os.path.join(rmq_config.rmq_home, 'var/log/rabbitmq', rmq_config.node_name + "@" + rmq_config.hostname.split('.')[0] + ".log"), log_name) if setup_type in ["all", "federation"]: # Create a multi-platform federation setup invalid = False _create_federation_setup(rmq_config.admin_user, rmq_config.admin_pwd, rmq_config.is_ssl, rmq_config.virtual_host, rmq_config.volttron_home) if setup_type in ["all", "shovel"]: # Create shovel setup invalid = False if rmq_config.is_ssl: port = rmq_config.amqp_port_ssl else: port = rmq_config.amqp_port _create_shovel_setup(rmq_config.instance_name, rmq_config.hostname, port, rmq_config.virtual_host, rmq_config.volttron_home, rmq_config.is_ssl) if invalid: _log.error("Unknown option. Exiting....")
def _create_rabbitmq_config(rmq_config, setup_type): """ Prompt user for required details and create a rabbitmq_config.yml file in volttron home :param setup_type: type of rmq setup - single, federation, shovel or all """ if setup_type == 'single' or setup_type == 'all': if os.path.exists(rmq_config.volttron_rmq_config): prompt = "rabbitmq_config.yml exists in {} Do you wish to " \ "use this file to configure the instance".format( get_home()) prompt = prompt_response(prompt, valid_answers=y_or_n, default='Y') if prompt in y: return else: _log.info("New input data will be used to overwrite existing " "{}".format(rmq_config.volttron_rmq_config)) # TODO: ideally we can load existing file and set values in it # default and the compare what changed. If rmq-home changed # and existing config those should get cleared. If cert details # get changed - overwrite ca, server, admin cert and delete all # other certs. rmq_config.rmq_home = _prompt_rmq_home(rmq_config.rabbitmq_server) prompt = 'Fully qualified domain name of the system:' new_host = prompt_response(prompt, default=getfqdn()) rmq_config.hostname = new_host rmq_config.is_ssl = True if rmq_config.is_ssl: prompt = "Would you like to create a new self signed root CA" \ "certificate for this instance:" prompt = prompt_response(prompt, valid_answers=y_or_n, default='Y') if prompt in y: cert_data = {} print( "\nPlease enter the following details for root CA certificate") prompt = '\tCountry:' cert_data['country'] = prompt_response(prompt, default='US') prompt = '\tState:' cert_data['state'] = prompt_response(prompt, mandatory=True) prompt = '\tLocation:' cert_data['location'] = prompt_response(prompt, mandatory=True) prompt = '\tOrganization:' cert_data['organization'] = prompt_response(prompt, mandatory=True) prompt = '\tOrganization Unit:' cert_data['organization-unit'] = prompt_response(prompt, mandatory=True) cert_data['common-name'] = rmq_config.instance_name + '-root-ca' rmq_config.certificate_data = cert_data else: error = True while error: while True: prompt = 'Enter the root CA certificate public key file:' root_public = prompt_response(prompt, mandatory=True) if is_file_readable(root_public): break while True: prompt =\ 'Enter the root CA certificate private key file:' root_key = prompt_response(prompt, mandatory=True) if is_file_readable(root_key): break if certs.Certs.validate_key_pair(root_public, root_key): error = False cert_data = { 'ca-public-key': root_public, 'ca-private-key': root_key } rmq_config.certificate_data = cert_data else: print("Error: Given public key and private key do not " "match or is invalid. public and private key " "files should be PEM encoded and private key " "should use RSA encryption") prompt = "Do you want to use default values for RabbitMQ home, " \ "ports, and virtual host:" prompt = prompt_response(prompt, valid_answers=y_or_n, default='Y') if prompt in y: rmq_config.amqp_port = '5672' rmq_config.mgmt_port = '15672' rmq_config.amqp_port_ssl = '5671' rmq_config.mgmt_port_ssl = '15671' rmq_config.virtual_host = 'volttron' else: rmq_config.virtual_host = _prompt_vhost(rmq_config.config_opts) prompt = 'AMQP port for RabbitMQ:' rmq_config.amqp_port = prompt_port(5672, prompt) prompt = 'http port for the RabbitMQ management plugin:' rmq_config.mgmt_port = prompt_port(15672, prompt) if rmq_config.is_ssl: prompt = 'AMQPS (SSL) port RabbitMQ address:' rmq_config.amqp_port_ssl = prompt_port(5671, prompt) prompt = 'https port for the RabbitMQ management plugin:' rmq_config.mgmt_port_ssl = prompt_port(15671, prompt) # Write the new config options back to config file rmq_config.write_rmq_config() if setup_type in ['federation', 'all']: # if option was all then config_opts would be not null # if this was called with just setup_type = federation, load existing # config so that we don't overwrite existing federation configs prompt_upstream_servers(rmq_config.volttron_home) if setup_type in ['shovel', 'all']: # if option was all then config_opts would be not null # if this was called with just setup_type = shovel, load existing # config so that we don't overwrite existing list prompt_shovels(rmq_config.volttron_home)
def wizard(): global config_opts """Routine for configuring an installed volttron instance. The function interactively sets up the instance for working with volttron central and the discovery service. """ # Start true configuration here. volttron_home = get_home() confirm_volttron_home() _load_config() _update_config_file() do_message_bus() do_vip() do_instance_name() _update_config_file() prompt = 'Is this instance web enabled?' response = prompt_response(prompt, valid_answers=y_or_n, default='N') if response in y: if not _check_dependencies_met('web'): print("Web dependencies not installed. Installing now...") set_dependencies('web') print("Done!") if config_opts['message-bus'] == 'rmq': do_web_enabled_rmq(volttron_home) elif config_opts['message-bus'] == 'zmq': do_web_enabled_zmq(volttron_home) _update_config_file() # TODO: Commented out so we don't prompt for installing vc or vcp until they # have been figured out totally for python3 prompt = 'Is this an instance of volttron central?' response = prompt_response(prompt, valid_answers=y_or_n, default='N') if response in y: do_vc() if _is_agent_installed('vc'): print( "VC admin and password are set up using the admin web interface.\n" "After starting VOLTTRON, please go to {} to complete the setup." .format( os.path.join(config_opts['bind-web-address'], "admin", "login.html"))) prompt = 'Will this instance be controlled by volttron central?' response = prompt_response(prompt, valid_answers=y_or_n, default='Y') if response in y: if not _check_dependencies_met( "drivers") or not _check_dependencies_met("web"): print("VCP dependencies not installed. Installing now...") if not _check_dependencies_met("drivers"): set_dependencies("drivers") if not _check_dependencies_met("web"): set_dependencies("web") print("Done!") do_vcp() prompt = 'Would you like to install a platform historian?' response = prompt_response(prompt, valid_answers=y_or_n, default='N') if response in y: do_platform_historian() prompt = 'Would you like to install a platform driver?' response = prompt_response(prompt, valid_answers=y_or_n, default='N') if response in y: if not _check_dependencies_met("drivers"): print("Driver dependencies not installed. Installing now...") set_dependencies("drivers") print("Done!") do_platform_driver() prompt = 'Would you like to install a listener agent?' response = prompt_response(prompt, valid_answers=y_or_n, default='N') if response in y: do_listener() print('Finished configuration!\n') print('You can now start the volttron instance.\n') print('If you need to change the instance configuration you can edit') print('the config file is at {}/config\n'.format(volttron_home))
def prompt_shovels(vhome): """ Prompt for shovel configuration and save in rabbitmq_shovel_config.yml :return: """ shovel_config_file = os.path.join(vhome, 'rabbitmq_shovel_config.yml') if os.path.exists(shovel_config_file): shovel_config = _read_config_file(shovel_config_file) else: shovel_config = {} shovels = shovel_config.get('shovels', {}) prompt = 'Number of destination hosts to configure:' count = prompt_response(prompt, default=1) count = int(count) i = 0 try: for i in range(0, count): prompt = 'Hostname of the destination server: ' host = prompt_response(prompt, mandatory=True) prompt = 'Port of the destination server: ' port = prompt_response(prompt, default=5671) prompt = 'Virtual host of the destination server: ' vhost = prompt_response(prompt, default='volttron') shovels[host] = {'port': port, 'virtual-host': vhost} prompt = prompt_response('\nDo you want shovels for ' 'PUBSUB communication? ', valid_answers=y_or_n, default='N') if prompt in y: prompt = 'Name of the agent publishing the topic:' agent_id = prompt_response(prompt, mandatory=True) prompt = 'List of PUBSUB topics to publish to ' \ 'this remote instance (comma seperated)' topics = prompt_response(prompt, mandatory=True) topics = topics.split(",") shovels[host]['pubsub'] = {agent_id : topics} prompt = prompt_response( '\nDo you want shovels for RPC communication? ', valid_answers=y_or_n, default='N') if prompt in y: prompt = 'Name of the remote instance: ' remote_instance = prompt_response(prompt, mandatory=True) prompt = 'Number of Local to Remote pairs:' agent_count = prompt_response(prompt, default=1) agent_count = int(agent_count) agent_ids = [] for r in range(0, agent_count): prompt = 'Local agent that wants to make RPC' local_agent_id = prompt_response(prompt, mandatory=True) prompt = 'Remote agent on which to make the RPC' remote_agent_id = prompt_response(prompt, mandatory=True) agent_ids.append([local_agent_id, remote_agent_id]) shovels[host]['rpc'] = {remote_instance: agent_ids} except ValueError as e: _log.error("Invalid choice in the configuration: {}".format(e)) else: shovel_config['shovel'] = shovels _write_to_config_file(shovel_config_file, shovel_config)
def _prompt_vhost(config_opts): vhost = config_opts.get('virtual-host', 'volttron') prompt = 'Name of the virtual host under which RabbitMQ ' \ 'VOLTTRON will be running:' new_vhost = prompt_response(prompt, default=vhost) return new_vhost