Beispiel #1
0
    def build_agent_connection(self, identity, instance_name):
        """
        Check if RabbitMQ user and certs exists for this agent, if not
        create a new one. Add access control/permissions if necessary.
        Return connection parameters.
        :param identity: Identity of agent
        :param instance_name: instance name of the platform
        :param is_ssl: Flag to indicate if SSL connection or not
        :return: Return connection parameters
        """

        rmq_user = get_fq_identity(identity, instance_name)
        permissions = self.get_default_permissions(rmq_user)

        if self.is_ssl:
            self.rmq_config.crts.create_ca_signed_cert(rmq_user,
                                                       overwrite=False)
        param = None

        try:
            self.create_user_with_permissions(rmq_user,
                                              permissions,
                                              ssl_auth=self.is_ssl)
            param = self.build_connection_param(rmq_user, ssl_auth=self.is_ssl)
        except AttributeError:
            _log.error(
                "Unable to create RabbitMQ user for the agent. Check if RabbitMQ broker is running"
            )

        return param
Beispiel #2
0
    def send_alert(self, alert_key, statusobj):
        """
        An alert_key is a quasi-unique key.  A listener to the alert can
        determine whether to pass the alert on to a higher level based upon
        the frequency of this alert.

        :param alert_key:
        :param context:
        :return:
        """
        if not isinstance(statusobj, Status):
            raise ValueError('statusobj must be a Status object.')
        agent_class = self._owner.__class__.__name__
        fq_identity = get_fq_identity(self._core().identity)
        # RMQ and other message buses can't handle '.' because it's used as the separator.  This
        # causes us to change the alert topic's agent_identity to have '_' rather than '.'.
        topic = topics.ALERTS(agent_class=agent_class,
                              agent_identity=fq_identity.replace('.', '_'))
        headers = dict(alert_key=alert_key)

        self._owner.vip.pubsub.publish(
            "pubsub",
            topic=topic.format(),
            headers=headers,
            message=statusobj.as_json()).get(timeout=10)
Beispiel #3
0
    def __init__(self,
                 url,
                 identity,
                 instance_name,
                 reconnect_delay=30,
                 vc_url=None):
        """
        The `RMQConnection` class provides  a connection to the rabbitmq message bus for both local and
        remote VOLTTRON instances.  The idenity parameter must be non qualified from the reference
        of the VOLTTRON instance this class is connecting to.  In other words
        if this is a local connection the non qualified identity should be used.  But in the case of a remote
        connection the identity should be prefixed with the <localinstancename.>.  An example is as follows:

        With the following:
            - local agent identity: platform.agent
            - local instance name: v1
            - remote instance name: v2

        For the local connection to v1, platform.agent should be passed as the identy parameter to this class.  For
        a remote connection to v2, v1.platform.agent should be passed to the identity parameter.

        :param url:
        :param identity:
        :param instance_name:
        :param reconnect_delay:
        :param vc_url:
        """
        super(RMQConnection, self).__init__(url, identity, instance_name)
        self._connection = None
        self.channel = None
        self._closing = False
        self._consumer_tag = None
        self._error_tag = None

        if vc_url:
            self._url = url

        self._connection_param = url

        self.routing_key = self._vip_queue_name = self._rmq_userid = get_fq_identity(
            identity, instance_name)
        self.exchange = 'volttron'
        self._connect_callback = None
        self._connect_error_callback = None
        self._queue_properties = dict()
        self._explicitly_closed = False
        self._reconnect_delay = reconnect_delay
        self._vip_handler = None
        self._error_handler = None
        self._instance_name = instance_name
        self._identity = identity
Beispiel #4
0
    def install_agent(self, agent_wheel, vip_identity=None, publickey=None,
                      secretkey=None):

        if self.secure_agent_user:
            _log.info("Installing secure Volttron agent...")
        while True:
            agent_uuid = str(uuid.uuid4())
            if agent_uuid in self.agents:
                continue
            agent_path = os.path.join(self.install_dir, agent_uuid)
            try:
                os.mkdir(agent_path)
                break
            except OSError as exc:
                if exc.errno != errno.EEXIST:
                    raise
        try:
            if auth is not None and self.env.verify_agents:
                unpacker = auth.VolttronPackageWheelFile(agent_wheel,
                                                         certsobj=certs.Certs())
                unpacker.unpack(dest=agent_path)
            else:
                unpack(agent_wheel, dest=agent_path)

            # Is it ok to remove the wheel file after unpacking?
            os.remove(agent_wheel)

            final_identity = self._setup_agent_vip_id(
                agent_uuid, vip_identity=vip_identity)
            keystore = self.get_agent_keystore(agent_uuid, publickey, secretkey)

            self._authorize_agent_keys(agent_uuid, final_identity, keystore.public)

            if self.message_bus == 'rmq':
                rmq_user = get_fq_identity(final_identity,
                                           self.instance_name)
                certs.Certs().create_signed_cert_files(rmq_user, overwrite=False)

            if self.secure_agent_user:
                # When installing, we always create a new user, as anything
                # that already exists is untrustworthy
                created_user = self.add_agent_user(self.agent_name(agent_uuid),
                                                   agent_path)
                self.set_agent_user_permissions(created_user,
                                                agent_uuid,
                                                agent_path)
        except Exception:
            shutil.rmtree(agent_path)
            raise
        return agent_uuid
Beispiel #5
0
def test_unhandled_cache_read_exception(volttron_instance, weather,
                                        query_agent):
    try:
        location = {"location": "fake_location"}
        query_agent.alert_callback.reset_mock()
        results1 = query_agent.vip.rpc.call(identity,
                                            "get_current_weather",
                                            [location]).get(timeout=10)[0]
        # results should be got from remote
        assert results1['weather_results']
        # cache should be working
        assert query_agent.alert_callback.call_count == 0
        # closing the sqlite connection will force reads and writes to fail
        weather._cache.close()
        gevent.sleep(1)
        results2 = query_agent.vip.rpc.call(identity,
                                            "get_current_weather",
                                            [location]).get(timeout=10)[0]
        gevent.sleep(1)
        assert query_agent.alert_callback.call_count == 2
        first_call = query_agent.alert_callback.call_args_list[0][0]
        second_call = query_agent.alert_callback.call_args_list[1][0]
        fq_identity = get_fq_identity(weather.core.identity).replace('.', '_')
        assert first_call[3] == second_call[3] == \
               "alerts/BasicWeatherAgent/{}".format(fq_identity)
        assert first_call[4]['alert_key'] == \
            "Cache read failed"
        assert ujson.loads(first_call[5])['context'] == \
            "Weather agent failed to read from cache"
        assert second_call[4]['alert_key'] == "Cache write failed"
        assert ujson.loads(second_call[5])['context'] == \
            "Weather agent failed to write to cache"
        # results should be retrieved from the remote api
        assert len(results2["weather_results"]["points"]) == 4
        # results should not have the same timestamps
        assert results1["observation_time"] != results2["observation_time"]
        # ensure the correct warning has been given
        read_warning = False
        for warning in results2["weather_warnings"]:
            if warning == "Weather agent failed to read from cache":
                read_warning = True
                break
        assert read_warning
    finally:
        # make sure the cache is ready to be used again
        weather._cache._sqlite_conn = sqlite3.connect(weather._database_file)
Beispiel #6
0
    def build_agent_connection(self, identity, instance_name):
        """
        Check if RabbitMQ user and certs exists for this agent, if not
        create a new one. Add access control/permissions if necessary.
        Return connection parameters.
        :param identity: Identity of agent
        :param instance_name: instance name of the platform
        :param is_ssl: Flag to indicate if SSL connection or not
        :return: Return connection parameters
        """

        rmq_user = get_fq_identity(identity, instance_name)
        permissions = self.get_default_permissions(rmq_user)

        if self.is_ssl:
            # This could fail with permission error when running in secure mode
            # and agent was installed when volttron was running on ZMQ instance
            # and then switched to RMQ instance. In that case
            # vctl certs create-ssl-keypair should be used to create a cert/key pair
            # and then agents should be started.
            try:
                _log.info("Creating ca signed certs for {}".format(rmq_user))
                self.rmq_config.crts.create_signed_cert_files(rmq_user,
                                                              overwrite=False)
            except Exception as e:
                _log.error("Exception creating certs. {}".format(e))
                raise RuntimeError(e)
        param = None

        try:
            root_ca_name, server_cert, admin_user = \
                certs.Certs.get_admin_cert_names(self.rmq_config.instance_name)
            if os.access(self.rmq_config.crts.private_key_file(admin_user),
                         os.R_OK):
                # this must be called from service agents. Create rmq user with permissions
                # for installed agent this would be done by aip at start of agent
                self.create_user_with_permissions(rmq_user,
                                                  permissions,
                                                  ssl_auth=self.is_ssl)
            param = self.build_connection_param(rmq_user, ssl_auth=self.is_ssl)
        except AttributeError:
            _log.error(
                "Unable to create RabbitMQ user for the agent. Check if RabbitMQ broker is running"
            )

        return param
Beispiel #7
0
    def startupagent(self, sender, **kwargs):

        import urlparse
        parsed = urlparse.urlparse(self.bind_web_address)

        ssl_key = self.web_ssl_key
        ssl_cert = self.web_ssl_cert

        if parsed.scheme == 'https':
            # Admin interface is only availble to rmq at present.
            if self.core.messagebus == 'rmq':
                self._admin_endpoints = AdminEndpoints(
                    self.core.rmq_mgmt,
                    self._certs.get_cert_public_key(
                        get_fq_identity(self.core.identity)))

            if ssl_key is None or ssl_cert is None:
                # Because the master.web service certificate is a client to rabbitmq we
                # can't use it directly therefore we use the -server on the file to specify
                # the server based file.
                base_filename = get_fq_identity(self.core.identity) + "-server"
                ssl_cert = self._certs.cert_file(base_filename)
                ssl_key = self._certs.private_key_file(base_filename)

                if not os.path.isfile(ssl_cert) or not os.path.isfile(ssl_key):
                    self._certs.create_ca_signed_cert(base_filename,
                                                      type='server')

        hostname = parsed.hostname
        port = parsed.port

        _log.info('Starting web server binding to {}://{}:{}.'.format(
            parsed.scheme, hostname, port))
        # Handle the platform.web routes here.
        self.registeredroutes.append(
            (re.compile('^/discovery/$'), 'callable', self._get_discovery))
        self.registeredroutes.append(
            (re.compile('^/discovery/allow$'), 'callable', self._allow))
        # these routes are only available for rmq based message bus
        # at present.
        if self.core.messagebus == 'rmq':
            # We need reference to the object so we can change the behavior of
            # whether or not to have auto certs be created or not.
            self._csr_endpoints = CSREndpoints(self.core)
            for rt in self._csr_endpoints.get_routes():
                self.registeredroutes.append(rt)

            for rt in self._admin_endpoints.get_routes():
                self.registeredroutes.append(rt)

            ssl_private_key = self._certs.get_private_key(
                get_fq_identity(self.core.identity))

            for rt in AuthenticateEndpoints(ssl_private_key).get_routes():
                self.registeredroutes.append(rt)

        static_dir = os.path.join(os.path.dirname(__file__), "static")
        self.registeredroutes.append((re.compile('^/.*$'), 'path', static_dir))

        port = int(port)
        vhome = os.environ.get('VOLTTRON_HOME')
        logdir = os.path.join(vhome, "log")
        if not os.path.exists(logdir):
            os.makedirs(logdir)

        self.appContainer = WebApplicationWrapper(self, hostname, port)
        if ssl_key and ssl_cert:
            svr = WSGIServer((hostname, port),
                             self.appContainer,
                             certfile=ssl_cert,
                             keyfile=ssl_key)
        else:
            svr = WSGIServer((hostname, port), self.appContainer)
        self._server_greenlet = gevent.spawn(svr.serve_forever)
Beispiel #8
0
    def app_routing(self, env, start_response):
        """
        The main routing function that maps the incoming request to a response.

        Depending on the registered routes map the request data onto an rpc
        function or a specific named file.
        """
        path_info = env['PATH_INFO']

        if path_info.startswith('/http://'):
            path_info = path_info[path_info.index('/', len('/http://')):]

        # only expose a partial list of the env variables to the registered
        # agents.
        envlist = [
            'HTTP_USER_AGENT', 'PATH_INFO', 'QUERY_STRING', 'REQUEST_METHOD',
            'SERVER_PROTOCOL', 'REMOTE_ADDR', 'HTTP_ACCEPT_ENCODING',
            'HTTP_COOKIE', 'CONTENT_TYPE', 'HTTP_AUTHORIZATION', 'SERVER_NAME',
            'wsgi.url_scheme', 'HTTP_HOST'
        ]
        data = env['wsgi.input'].read()
        passenv = dict((envlist[i], env[envlist[i]])
                       for i in range(0, len(envlist))
                       if envlist[i] in env.keys())

        _log.debug('path_info is: {}'.format(path_info))
        # Get the peer responsible for dealing with the endpoint.  If there
        # isn't a peer then fall back on the other methods of routing.
        (peer, res_type) = self.endpoints.get(path_info, (None, None))
        _log.debug('Peer path_info is associated with: {}'.format(peer))

        if self.is_json_content(env):
            data = json.loads(data)

        # Only if https available and rmq for the admin area.
        if env['wsgi.url_scheme'] == 'https' and self.core.messagebus == 'rmq':
            # Load the publickey that was used to sign the login message through the env
            # parameter so agents can use it to verify the Bearer has specific
            # jwt claims
            passenv['WEB_PUBLIC_KEY'] = env[
                'WEB_PUBLIC_KEY'] = self._certs.get_cert_public_key(
                    get_fq_identity(self.core.identity))

        # if we have a peer then we expect to call that peer's web subsystem
        # callback to perform whatever is required of the method.
        if peer:
            _log.debug('Calling peer {} back with env={} data={}'.format(
                peer, passenv, data))
            res = self.vip.rpc.call(peer, 'route.callback', passenv,
                                    data).get(timeout=60)

            if res_type == "jsonrpc":
                return self.create_response(res, start_response)
            elif res_type == "raw":
                return self.create_raw_response(res, start_response)

        env['JINJA2_TEMPLATE_ENV'] = tplenv

        # if ws4pi.socket is set then this connection is a web socket
        # and so we return the websocket response.
        if 'ws4py.socket' in env:
            return env['ws4py.socket'](env, start_response)

        for k, t, v in self.registeredroutes:
            if k.match(path_info):
                _log.debug(
                    "MATCHED:\npattern: {}, path_info: {}\n v: {}".format(
                        k.pattern, path_info, v))
                _log.debug('registered route t is: {}'.format(t))
                if t == 'callable':  # Generally for locally called items.
                    # Changing signature of the "locally" called points to return
                    # a Response object. Our response object then will in turn
                    # be processed and the response will be written back to the
                    # calling client.
                    try:
                        retvalue = v(env, start_response, data)
                    except TypeError:
                        retvalue = self.process_response(
                            start_response, v(env, data))

                    if isinstance(retvalue, Response):
                        return self.process_response(start_response, retvalue)
                    else:
                        return retvalue

                elif t == 'peer_route':  # RPC calls from agents on the platform
                    _log.debug('Matched peer_route with pattern {}'.format(
                        k.pattern))
                    peer, fn = (v[0], v[1])
                    res = self.vip.rpc.call(peer, fn, passenv,
                                            data).get(timeout=120)
                    _log.debug(res)
                    return self.create_response(res, start_response)

                elif t == 'path':  # File service from agents on the platform.
                    if path_info == '/':
                        return self._redirect_index(env, start_response)
                    server_path = v + path_info  # os.path.join(v, path_info)
                    _log.debug('Serverpath: {}'.format(server_path))
                    return self._sendfile(env, start_response, server_path)

        start_response('404 Not Found', [('Content-Type', 'text/html')])
        return [b'<h1>Not Found</h1>']
Beispiel #9
0
    def connect_remote_platform(
            self,
            address,
            serverkey=None,
            agent_class=None
    ):
        """
        Agent attempts to connect to a remote platform to exchange data.

        address must start with http, https, tcp, ampq, or ampqs or a
        ValueError will be
        raised

        If this function is successful it will return an instance of the
        `agent_class`
        parameter if not then this function will return None.

        If the address parameter begins with http or https
        TODO: use the known host functionality here
        the agent will attempt to use Discovery to find the values
        associated with it.

        Discovery should return either an rmq-address or a vip-address or
        both.  In
        that situation the connection will be made using zmq.  In the event
        that
        fails then rmq will be tried.  If both fail then None is returned
        from this
        function.

        """
        from volttron.platform.vip.agent.utils import build_agent
        from volttron.platform.vip.agent import Agent

        if agent_class is None:
            agent_class = Agent

        parsed_address = urlparse(address)
        _log.debug("Begining auth.connect_remote_platform: {}".format(address))

        value = None
        if parsed_address.scheme == "tcp":
            # ZMQ connection
            hosts = KnownHostsStore()
            temp_serverkey = hosts.serverkey(address)
            if not temp_serverkey:
                _log.info(
                    "Destination serverkey not found in known hosts file, "
                    "using config"
                )
                destination_serverkey = serverkey
            elif not serverkey:
                destination_serverkey = temp_serverkey
            else:
                if temp_serverkey != serverkey:
                    raise ValueError(
                        "server_key passed and known hosts serverkey do not "
                        ""
                        "match!"
                    )
                destination_serverkey = serverkey

            publickey, secretkey = (
                self._core().publickey,
                self._core().secretkey,
            )
            _log.debug(
                "Connecting using: %s", get_fq_identity(self._core().identity)
            )

            value = build_agent(
                agent_class=agent_class,
                identity=get_fq_identity(self._core().identity),
                serverkey=destination_serverkey,
                publickey=publickey,
                secretkey=secretkey,
                message_bus="zmq",
                address=address,
            )
        elif parsed_address.scheme in ("https", "http"):
            from volttron.platform.web import DiscoveryInfo
            from volttron.platform.web import DiscoveryError

            try:
                # TODO: Use known host instead of looking up for discovery
                #  info if possible.

                # We need to discover which type of bus is at the other end.
                info = DiscoveryInfo.request_discovery_info(address)
                remote_identity = "{}.{}.{}".format(
                    info.instance_name,
                    get_platform_instance_name(),
                    self._core().identity,
                )
                # if the current message bus is zmq then we need
                # to connect a zmq on the remote, whether that be the
                # rmq router or proxy.  Also note that we are using the
                # fully qualified
                # version of the identity because there will be conflicts if
                # volttron central has more than one platform.agent connecting
                if get_messagebus() == "zmq":
                    if not info.vip_address or not info.serverkey:
                        err = (
                            "Discovery from {} did not return serverkey "
                            "and/or vip_address".format(address)
                        )
                        raise ValueError(err)

                    _log.debug(
                        "Connecting using: %s",
                        get_fq_identity(self._core().identity),
                    )

                    # use fully qualified identity
                    value = build_agent(
                        identity=get_fq_identity(self._core().identity),
                        address=info.vip_address,
                        serverkey=info.serverkey,
                        secretkey=self._core().secretkey,
                        publickey=self._core().publickey,
                        agent_class=agent_class,
                    )

                else:  # we are on rmq messagebus

                    # This is if both remote and local are rmq message buses.
                    if info.messagebus_type == "rmq":
                        _log.debug("Both remote and local are rmq messagebus.")
                        fqid_local = get_fq_identity(self._core().identity)

                        # Check if we already have the cert, if so use it
                        # instead of requesting cert again
                        remote_certs_dir = self.get_remote_certs_dir()
                        remote_cert_name = "{}.{}".format(
                            info.instance_name, fqid_local
                        )
                        certfile = os.path.join(
                            remote_certs_dir, remote_cert_name + ".crt"
                        )
                        if os.path.exists(certfile):
                            response = certfile
                        else:
                            response = self.request_cert(
                                address, fqid_local, info
                            )

                        if response is None:
                            _log.error("there was no response from the server")
                            value = None
                        elif isinstance(response, tuple):
                            if response[0] == "PENDING":
                                _log.info(
                                    "Waiting for administrator to accept a "
                                    "CSR request."
                                )
                            value = None
                        # elif isinstance(response, dict):
                        #     response
                        elif os.path.exists(response):
                            # info = DiscoveryInfo.request_discovery_info(
                            # address)
                            # From the remote platforms perspective the
                            # remote user name is
                            #   remoteinstance.localinstance.identity,
                            #   this is what we must
                            #   pass to the build_remote_connection_params
                            #   for a successful

                            remote_rmq_user = get_fq_identity(
                                fqid_local, info.instance_name
                            )
                            _log.debug(
                                "REMOTE RMQ USER IS: %s", remote_rmq_user
                            )
                            remote_rmq_address = self._core().rmq_mgmt.build_remote_connection_param(
                                remote_rmq_user,
                                info.rmq_address,
                                ssl_auth=True,
                                cert_dir=self.get_remote_certs_dir(),
                            )

                            value = build_agent(
                                identity=fqid_local,
                                address=remote_rmq_address,
                                instance_name=info.instance_name,
                                publickey=self._core().publickey,
                                secretkey=self._core().secretkey,
                                message_bus="rmq",
                                enable_store=False,
                                agent_class=agent_class,
                            )
                        else:
                            raise ValueError(
                                "Unknown path through discovery process!"
                            )

                    else:
                        # TODO: cache the connection so we don't always have
                        #  to ping the server to connect.

                        # This branch happens when the message bus is not
                        # the same note
                        # this writes to the agent-data directory of this
                        # agent if the agent
                        # is installed.
                        if get_messagebus() == "rmq":
                            if not os.path.exists("keystore.json"):
                                with open("keystore.json", "w") as file_pointer:
                                    file_pointer.write(
                                        jsonapi.dumps(
                                            KeyStore.generate_keypair_dict()
                                        )
                                    )

                            with open("keystore.json") as file_pointer:
                                keypair = jsonapi.loads(file_pointer.read())

                        value = build_agent(
                            agent_class=agent_class,
                            identity=remote_identity,
                            serverkey=info.serverkey,
                            publickey=keypair.get("publickey"),
                            secretkey=keypair.get("secretekey"),
                            message_bus="zmq",
                            address=info.vip_address,
                        )
            except DiscoveryError:
                _log.error(
                    "Couldn't connect to %s or incorrect response returned "
                    "response was %s",
                    address,
                    value,
                )

        else:
            raise ValueError(
                "Invalid configuration found the address: {} has an invalid "
                "scheme".format(address)
            )

        return value
Beispiel #10
0
def get_user_claim_from_bearer(bearer):
    certs = Certs()
    pubkey = certs.get_cert_public_key(get_fq_identity(MASTER_WEB))
    return jwt.decode(bearer, pubkey, algorithms='RS256')
Beispiel #11
0
    def start_agent(self, agent_uuid):
        name = self.agent_name(agent_uuid)
        agent_dir = os.path.join(self.install_dir, agent_uuid)
        agent_path_with_name = os.path.join(agent_dir, name)
        execenv = self.agents.get(agent_uuid)
        if execenv and execenv.process.poll() is None:
            _log.warning('request to start already running agent %s',
                         agent_path_with_name)
            raise ValueError('agent is already running')

        pkg = UnpackedPackage(agent_path_with_name)
        if auth is not None and self.env.verify_agents:
            auth.UnpackedPackageVerifier(pkg.distinfo).verify()
        metadata = pkg.metadata
        try:
            exports = metadata['extensions']['python.exports']
        except KeyError:
            try:
                exports = metadata['exports']
            except KeyError:
                raise ValueError('no entry points exported')
        try:
            module = exports['volttron.agent']['launch']
        except KeyError:
            try:
                module = exports['setuptools.installation']['eggsecutable']
            except KeyError:
                _log.error('no agent launch class specified in package %s',
                           agent_path_with_name)
                raise ValueError('no agent launch class specified in package')
        config = os.path.join(pkg.distinfo, 'config')
        tag = self.agent_tag(agent_uuid)
        environ = os.environ.copy()
        environ['PYTHONPATH'] = ':'.join([agent_path_with_name] + sys.path)
        environ['PATH'] = (os.path.abspath(os.path.dirname(sys.executable)) +
                           ':' + environ['PATH'])
        if os.path.exists(config):
            environ['AGENT_CONFIG'] = config
        else:
            environ.pop('AGENT_CONFIG', None)
        if tag:
            environ['AGENT_TAG'] = tag
        else:
            environ.pop('AGENT_TAG', None)
        environ['AGENT_SUB_ADDR'] = self.subscribe_address
        environ['AGENT_PUB_ADDR'] = self.publish_address
        environ['AGENT_UUID'] = agent_uuid
        environ['_LAUNCHED_BY_PLATFORM'] = '1'

        # For backwards compatibility create the identity file if it does not
        # exist.
        identity_file = os.path.join(self.install_dir, agent_uuid, "IDENTITY")
        if not os.path.exists(identity_file):
            _log.debug(
                'IDENTITY FILE MISSING: CREATING IDENTITY FILE WITH VALUE: {}'.
                    format(agent_uuid))
            with open(identity_file, 'w') as fp:
                fp.write(agent_uuid)

        with open(identity_file, 'r') as fp:
            agent_vip_identity = fp.read()

        environ['AGENT_VIP_IDENTITY'] = agent_vip_identity

        module, _, func = module.partition(':')
        # if func:
        #     code = '__import__({0!r}, fromlist=[{1!r}]).{1}()'.format(module,
        #                                                               func)
        #     argv = [sys.executable, '-c', code]
        # else:
        argv = [sys.executable, '-m', module]
        resmon = getattr(self.env, 'resmon', None)
        agent_user = None

        data_dir = self._get_agent_data_dir(agent_path_with_name)

        if self.secure_agent_user:
            _log.info("Starting agent securely...")
            user_id_path = os.path.join(agent_dir, "USER_ID")
            try:
                with open(user_id_path, "r") as user_id_file:
                    volttron_agent_id = user_id_file.readline()
                    pwd.getpwnam(volttron_agent_id)
                    agent_user = volttron_agent_id
                    _log.info("Found secure volttron agent user {}".format(
                        agent_user))
            except (IOError, KeyError) as err:
                _log.info("No existing volttron agent user was found at {} due "
                          "to {}".format(user_id_path, err))

                # May be switched from normal to secure mode with existing agents. To handle this case
                # create users and also set permissions again for existing files
                agent_user = self.add_agent_user(name, agent_dir)
                self.set_agent_user_permissions(agent_user,
                                                agent_uuid,
                                                agent_dir)

                # additionally give permissions to contents of agent-data dir.
                # This is needed only for agents installed before switching to
                # secure mode. Agents installed in secure mode will own files
                # in agent-data dir
                # Moved this to the top so that "agent-data" directory gets
                # created in the beginning
                #data_dir = self._get_agent_data_dir(agent_path_with_name)

                for (root, directories, files) in os.walk(data_dir,
                                                          topdown=True):
                    for directory in directories:
                        self.set_acl_for_path("rwx", agent_user,
                                                  os.path.join(root, directory))
                    for f in files:
                        self.set_acl_for_path("rwx", agent_user,
                                              os.path.join(root, f))


        if self.message_bus == 'rmq':
            rmq_user = get_fq_identity(agent_vip_identity, self.instance_name)
            _log.info("Create RMQ user {} for agent {}".format(rmq_user, agent_vip_identity))

            self.rmq_mgmt.create_user_with_permissions(rmq_user, self.rmq_mgmt.get_default_permissions(rmq_user),
                                                       ssl_auth=True)
            key_file = certs.Certs().private_key_file(rmq_user)
            if not os.path.exists(key_file):
                # This could happen when user switches from zmq to rmq after installing agent
                _log.info(f"agent certs don't exists. creating certs for agent")
                certs.Certs().create_signed_cert_files(rmq_user, overwrite=False)

            if self.secure_agent_user:
                # give read access to user to its own private key file.
                self.set_acl_for_path("r", agent_user, key_file)

        if resmon is None:
            if agent_user:
                execenv = SecureExecutionEnvironment(agent_user=agent_user)
            else:
                execenv = ExecutionEnvironment()
        else:
            execreqs = self._read_execreqs(pkg.distinfo)
            execenv = self._reserve_resources(resmon, execreqs,
                                              agent_user=agent_user)
        execenv.name = name or agent_path_with_name
        _log.info('starting agent %s', agent_path_with_name)
        # data_dir = self._get_agent_data_dir(agent_path_with_name)
        _log.info("starting agent using {} ".format(type(execenv)))
        execenv.execute(argv, cwd=agent_path_with_name, env=environ, close_fds=True,
                        stdin=open(os.devnull), stdout=PIPE, stderr=PIPE)
        self.agents[agent_uuid] = execenv
        proc = execenv.process
        _log.info('agent %s has PID %s', agent_path_with_name, proc.pid)
        gevent.spawn(log_stream, 'agents.stderr', name, proc.pid, argv[0],
                      log_entries('agents.log', name, proc.pid, logging.ERROR,
                                  proc.stderr))
        gevent.spawn(log_stream, 'agents.stdout', name, proc.pid, argv[0],
                   ((logging.INFO, line) for line in (l.splitlines() for l
                      in proc.stdout)))

        return self.agent_status(agent_uuid)