예제 #1
0
파일: main.py 프로젝트: GOsa3/gosa
    def handle_request(self, request_handler):
        foreman = PluginRegistry.getInstance("Foreman")
        data = loads(request_handler.request.body)
        self.log.debug(data)

        # TODO disable hook logging to file
        with open("foreman-log.json", "a") as f:
            f.write("%s,\n" % dumps(data, indent=4, sort_keys=True))

        if data["event"] in ForemanHookReceiver.skip_next_event and data["object"] in ForemanHookReceiver.skip_next_event[data["event"]]:
            ForemanHookReceiver.skip_next_event[data["event"]].remove(data["object"])
            self.log.info("skipped '%s' event for object: '%s'" % (data["event"], data["object"]))
            return

        data_keys = list(data['data'].keys())
        if len(data_keys) == 1:
            type = data_keys[0]
        else:
            # no type given -> skipping this event as other might come with more information
            self.log.warning("skipping event '%s' for object '%s' as no type information is given in data: '%s'" % (data["event"],
                                                                                                                  data["object"],
                                                                                                                  data["data"]))
            return

        # search for real data
        if len(data['data'][type].keys()) == 1:
            # something like {data: 'host': {host: {...}}}
            #             or {data: 'discovered_host': {host: {...}}}
            payload_data = data['data'][type][list(data['data'][type].keys())[0]]
        else:
            payload_data = data['data'][type]

        if type == "operatingsystem":
            with make_session() as session:
                foreman.sync_release_name(payload_data, session, event=data['event'])
                session.commit()
                return

        factory = ObjectFactory.getInstance()
        foreman_type = type
        if type == "discovered_host":
            type = "host"

        object_types = factory.getObjectNamesWithBackendSetting("Foreman", "type", "%ss" % type)
        object_type = object_types[0] if len(object_types) else None

        backend_attributes = factory.getObjectBackendProperties(object_type) if object_type is not None else None
        self.log.debug("Hookevent: '%s' for '%s' (%s)" % (data['event'], data['object'], object_type))

        uuid_attribute = None
        if "Foreman" in backend_attributes:
            uuid_attribute = backend_attributes["Foreman"]["_uuidSourceAttribute"] \
                if '_uuidSourceAttribute' in backend_attributes["Foreman"] else backend_attributes["Foreman"]["_uuidAttribute"]

        ForemanBackend.modifier = "foreman"
        update_data = {}

        if data['event'] in ["update", "create"] and foreman_type == "host":
            id = payload_data["id"] if "id" in payload_data else None
            try:
                foreman.write_parameters(id if id is not None else data['object'])
            except:
                foreman.mark_for_parameter_setting(data['object'], {
                    "status": "created",
                    "use_id": id
                })

        if data['event'] == "after_commit" or data['event'] == "update" or data['event'] == "after_create" or data['event'] == "create":
            host = None
            if data['event'] == "update" and foreman_type == "host" and "mac" in payload_data and payload_data["mac"] is not None:
                # check if we have an discovered host for this mac
                index = PluginRegistry.getInstance("ObjectIndex")
                res = index.search({
                    "_type": "Device",
                    "extension": ["ForemanHost", "ieee802Device"],
                    "macAddress": payload_data["mac"],
                    "status": "discovered"
                }, {"dn": 1})

                if len(res):
                    self.log.debug("update received for existing host with dn: %s" % res[0]["dn"])
                    host = ObjectProxy(res[0]["dn"])

                    if foreman_type != "discovered_host" and host.is_extended_by("ForemanHost"):
                        host.status = "unknown"

            foreman_object = foreman.get_object(object_type, payload_data[uuid_attribute], create=host is None)
            if foreman_object and host:
                if foreman_object != host:
                    self.log.debug("using known host instead of creating a new one")
                    # host is the formerly discovered host, which might have been changed in GOsa for provisioning
                    # so we want to use this one, foreman_object is the joined one, so copy the credentials from foreman_object to host
                    if not host.is_extended_by("RegisteredDevice"):
                        host.extend("RegisteredDevice")
                    if not host.is_extended_by("simpleSecurityObject"):
                        host.extend("simpleSecurityObject")
                    host.deviceUUID = foreman_object.deviceUUID
                    host.userPassword = foreman_object.userPassword
                    host.otp = foreman_object.otp
                    host.cn = foreman_object.cn

                    # now delete the formerly joined host
                    foreman_object.remove()
                    foreman_object = host

            elif foreman_object is None and host is not None:
                foreman_object = host

            elif foreman_type == "discovered_host":
                self.log.debug("setting discovered state for %s" % payload_data[uuid_attribute])
                if not foreman_object.is_extended_by("ForemanHost"):
                    foreman_object.extend("ForemanHost")
                foreman_object.status = "discovered"

            if foreman_type == "host":
                old_build_state = foreman_object.build

            foreman.update_type(object_type, foreman_object, payload_data, uuid_attribute, update_data=update_data)
            if foreman_type == "host" and old_build_state is True and foreman_object.build is False and \
                            foreman_object.status == "ready":
                # send notification
                e = EventMaker()
                ev = e.Event(e.Notification(
                    e.Title(N_("Host ready")),
                    e.Body(N_("Host '%s' has been successfully build." % foreman_object.cn)),
                    e.Icon("@Ligature/pc"),
                    e.Timeout("10000")
                ))
                event_object = objectify.fromstring(etree.tostring(ev, pretty_print=True).decode('utf-8'))
                SseHandler.notify(event_object)

        elif data['event'] == "after_destroy":
            # print("Payload: %s" % payload_data)
            foreman.remove_type(object_type, payload_data[uuid_attribute])

            # because foreman sends the after_commit event after the after_destroy event
            # we need to skip this event, otherwise the host would be re-created
            if "after_commit" not in ForemanHookReceiver.skip_next_event:
                ForemanHookReceiver.skip_next_event["after_commit"] = [data['object']]
            else:
                ForemanHookReceiver.skip_next_event["after_commit"].append(data['object'])

            # add garbage collection for skip
            sobj = PluginRegistry.getInstance("SchedulerService")
            sobj.getScheduler().add_date_job(self.cleanup_event_skipper,
                                             datetime.datetime.now() + datetime.timedelta(minutes=1),
                                             args=("after_commit", data['object']),
                                             tag='_internal', jobstore='ram')

        else:
            self.log.info("unhandled hook event '%s' received for '%s'" % (data['event'], type))

        ForemanBackend.modifier = None
예제 #2
0
    async def test_provision_host(self, m_get, m_del, m_put, m_post):
        """ convert a discovered host to a 'real' host  """
        self._test_dn = GosaTestCase.create_test_data()
        container = ObjectProxy(self._test_dn, "IncomingDeviceContainer")
        container.commit()

        mocked_foreman = MockForeman()
        m_get.side_effect = mocked_foreman.get
        m_del.side_effect = mocked_foreman.delete
        m_put.side_effect = mocked_foreman.put
        m_post.side_effect = mocked_foreman.post

        # create the discovered host + foremanHostgroup
        d_host = ObjectProxy(container.dn, "Device")
        d_host.cn = "mac00262df16a2c"
        d_host.extend("ForemanHost")
        d_host.status = "discovered"
        d_host.extend("ieee802Device")
        d_host.macAddress = "00:26:2d:f1:6a:2c"
        d_host.extend("IpHost")
        d_host.ipHostNumber = "192.168.0.1"
        d_host.commit()

        hostgroup = ObjectProxy("%s" % self._test_dn, "GroupOfNames")
        hostgroup.extend("ForemanHostGroup")
        hostgroup.cn = "Test"
        hostgroup.foremanGroupId = "4"
        hostgroup.commit()

        # add host to group
        logging.getLogger("test.foreman-integration").info("########### START: Add Host to group ############# %s" % AsyncHTTPTestCase.get_url(self, "/hooks/"))
        d_host = ObjectProxy("cn=mac00262df16a2c,%s" % container.dn)

        def check():
            logging.getLogger("test.foreman-integration").info("check condition: %s, %s" % (d_host.cn, d_host.status))
            return d_host.cn == "mac00262df16a2c" and d_host.status == "discovered"

        def check2():
            logging.getLogger("test.foreman-integration").info("check2 condition: %s" % d_host.cn)
            return d_host.cn == "Testhost"

        base_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data")
        with open(os.path.join(base_dir, "discovered_hosts", "mac00262df16a2c.json")) as f:
            mocked_foreman.register_conditional_response("http://localhost:8000/api/v2/discovered_hosts/mac00262df16a2c",
                                                         "get",
                                                         check,
                                                         f.read())
        with open(os.path.join(base_dir, "conditional", "Testhost.json")) as f:
            mocked_foreman.register_conditional_response("http://localhost:8000/api/v2/hosts/Testhost",
                                                         "get",
                                                         check2,
                                                         f.read())

        def activate(**kwargs):
            return True

        mocked_foreman.register_trigger("http://localhost:8000/api/v2/discovered_hosts/mac00262df16a2c",
                                        "put",
                                        activate,
                                        self.execute)

        with make_session() as session:
            assert session.query(ObjectInfoIndex.dn)\
                       .join(ObjectInfoIndex.properties)\
                       .filter(and_(KeyValueIndex.key == "cn", KeyValueIndex.value == "Testhost"))\
                       .count() == 0

        d_host.cn = "Testhost"
        d_host.groupMembership = hostgroup.dn
        d_host.commit()
        logging.getLogger("test.foreman-integration").info("waiting for 2 seconds")
        await asyncio.sleep(2)

        logging.getLogger("test.foreman-integration").info("########### END: Add Host to group #############")

        # now move the host to the final destination
        d_host = ObjectProxy("cn=Testhost,ou=incoming,%s" % self._test_dn)
        assert d_host.status != "discovered"
        assert d_host.name == "Testhost"
        assert d_host.hostgroup_id == "4"
        assert d_host.is_extended_by("RegisteredDevice") is True
        assert len(d_host.userPassword[0]) > 0
        assert d_host.deviceUUID is not None

        with make_session() as session:
            assert session.query(ObjectInfoIndex.dn) \
                       .join(ObjectInfoIndex.properties) \
                       .filter(and_(KeyValueIndex.key == "cn", KeyValueIndex.value == "Testhost")) \
                       .count() == 1

        logging.getLogger("test.foreman-integration").info("########### START: moving host #############")
        d_host.move("%s" % self._test_dn)
        logging.getLogger("test.foreman-integration").info("########### END: moving host #############")

        # lets check if everything is fine in the database
        d_host = ObjectProxy("cn=Testhost,ou=devices,%s" % self._test_dn, read_only=True)
        assert d_host is not None
        assert d_host.status == "unknown"
        assert d_host.groupMembership == hostgroup.dn
예제 #3
0
파일: main.py 프로젝트: GOsa3/gosa
    def add_host(self, hostname, base=None):

        # create dn
        if base is None:
            index = PluginRegistry.getInstance("ObjectIndex")
            # get the IncomingDevice-Container
            res = index.search({"_type": "IncomingDeviceContainer", "_parent_dn": self.type_bases["ForemanHost"]}, {"dn": 1})
            if len(res) > 0:
                base = res[0]["dn"]
            else:
                base = self.type_bases["ForemanHost"]

        ForemanBackend.modifier = "foreman"

        device = self.get_object("ForemanHost", hostname, create=False)
        if device is None:
            self.log.debug("Realm request: creating new host with hostname: %s" % hostname)
            device = ObjectProxy(base, "Device")
            device.extend("ForemanHost")
            device.cn = hostname
            # commit now to get a uuid
            device.commit()

            # re-open to get a clean object
            device = ObjectProxy(device.dn)
        else:
            self.log.debug("Realm request: use existing host with hostname: %s" % hostname)

        try:

            if not device.is_extended_by("ForemanHost"):
                device.extend("ForemanHost")

            # Generate random client key
            h, key, salt = generate_random_key()

            # While the client is going to be joined, generate a random uuid and an encoded join key
            if not device.is_extended_by("RegisteredDevice"):
                device.extend("RegisteredDevice")
            if not device.is_extended_by("simpleSecurityObject"):
                device.extend("simpleSecurityObject")
            if device.deviceUUID is None:
                cn = str(uuid.uuid4())
                device.deviceUUID = cn
            else:
                cn = device.deviceUUID

            device.status = "pending"
            device.status_InstallationInProgress = True

            if device.userPassword is None:
                device.userPassword = []
            elif device.otp is not None:
                device.userPassword.remove(device.otp)
            device.userPassword.append("{SSHA}" + encode(h.digest() + salt).decode())

            # make sure the client has the access rights he needs
            client_service = PluginRegistry.getInstance("ClientService")
            client_service.applyClientRights(device.deviceUUID)

            device.commit()
            self.mark_for_parameter_setting(hostname, {"status": "added"})
            return "%s|%s" % (key, cn)

        except Exception as e:
            # remove created device again because something went wrong
            # self.remove_type("ForemanHost", hostname)
            self.log.error(str(e))
            raise e

        finally:
            ForemanBackend.modifier = None