Skip to content

Commit

Permalink
Merge pull request #20 from diggyk/master
Browse files Browse the repository at this point in the history
Adding support for creating a bunch of events for an given query
  • Loading branch information
gmjosack committed Jun 30, 2015
2 parents 9b3370f + 4e65e1c commit c491727
Show file tree
Hide file tree
Showing 4 changed files with 141 additions and 37 deletions.
32 changes: 25 additions & 7 deletions bin/hermes
Original file line number Diff line number Diff line change
Expand Up @@ -506,8 +506,8 @@ def list_labors(args):
def create_event(args):
logging.debug("create_event()")
logging.debug(
"host: %s category: %s state: %s note: %s",
args.hostname, args.category, args.state, args.note
"host: %s category: %s state: %s note: %s query: %s",
args.hostname, args.category, args.state, args.note, args.query
)

response = request_get("/api/v1/eventtypes?limit=all")
Expand All @@ -531,16 +531,25 @@ def create_event(args):
user = getpass.getuser()

json = {
"hostname": args.hostname,
"user": user,
"eventTypeId": found_event_type["id"],
"note": args.note
}

if args.hostname:
json["hostname"] = args.hostname

if args.query:
json["hostQuery"] = args.query


response = request_post("/api/v1/events", json)

if response.json()["status"] == "created":
print "Created event {}".format(response.json()["id"])
if "totalEvents" in response.json():
print "Created {} events".format(response.json()["totalEvents"])
else:
print "Created event {}".format(response.json()["id"])
else:
sys.exit(
"Received unexpected status {}".format(response.json()["status"])
Expand Down Expand Up @@ -734,10 +743,12 @@ def create_quest(args):
json = {
"eventTypeId": found_event_type["id"],
"creator": user,
"description": args.description,
"hostnames": hosts
"description": args.description
}

if hosts:
json["hostnames"] = hosts

if args.query:
json["hostQuery"] = args.query

Expand Down Expand Up @@ -920,12 +931,19 @@ def parse_cli_args():
)
event_subparser = event_parser.add_subparsers()
event_create_parser = event_subparser.add_parser("create")
event_create_parser.add_argument("hostname")
event_create_parser.add_argument("category")
event_create_parser.add_argument("state")
event_create_parser.add_argument(
"-n", "--note", type=str, help="Note to attach to the Event"
)
event_create_parser.add_argument(
"--host", "--hostname", type=str, dest="hostname",
help="Host for which to create this Event"
)
event_create_parser.add_argument(
"--query", type=str,
help="Query for Hosts for which to create this Event"
)
event_create_parser.set_defaults(func=create_event)

# monitoring ping command line parser
Expand Down
130 changes: 104 additions & 26 deletions hermes/handlers/api.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import ipaddress
import logging
import random
import re
import sqlalchemy
from sqlalchemy import desc
from sqlalchemy.exc import IntegrityError
import string
import time


from .util import ApiHandler, PluginHelper
Expand Down Expand Up @@ -795,6 +797,7 @@ def post(self):
Content-Type: application/json
{
"hostname": "example",
"hostQuery": "tag=value",
"user": "johnny",
"eventTypeId": 3,
"note": "Sample description"
Expand All @@ -809,16 +812,33 @@ def post(self):
{
"status": "created",
"data": {
"event": {
"id": 1,
"href": "/api/v1/events/1",
"hostname": "example",
"user": "johnny",
"eventTypeId": 3,
"note": "Sample description"
}
}
"id": 1,
"href": "/api/v1/events/1",
"hostname": "example",
"user": "johnny",
"eventTypeId": 3,
"note": "Sample description"
}
or
.. sourcecode:: http
HTTP/1.1 201 OK
Location: /api/v1/events/1
{
"status": "created",
"events": [{
"id": 1,
"href": "/api/v1/events/1",
"hostname": "example",
"user": "johnny",
"eventTypeId": 3,
"note": "Sample description"
},
...
]
}
:reqjson string hostname: The hostname of the Host of this Event
Expand All @@ -838,12 +858,15 @@ def post(self):
"""

try:
hostname = self.jbody["hostname"]
hostnames = (
[self.jbody.get("hostname", None)]
if self.jbody.get("hostname", None) else []
)
user = self.jbody["user"]
if not EMAIL_REGEX.match(user):
user += "@" + self.domain
event_type_id = self.jbody["eventTypeId"]
note = self.jbody["note"]
note = self.jbody.get("note", None)
except KeyError as err:
raise exc.BadRequest(
"Missing Required Argument: {}".format(err.message)
Expand All @@ -857,26 +880,82 @@ def post(self):
self.write_error(400, message="Bad event type")
return

host = Host.get_host(self.session, hostname)
# If a host query was specified, we need to talk to the external
# query server to resolve this into a list of hostnames
if "hostQuery" in self.jbody:
query = self.jbody["hostQuery"]
response = PluginHelper.request_get(params={"query": query})
if response.json()["status"] == "ok":
hostnames.extend(response.json()["results"])

# We need to create a list of hostnames that don't have a Host record
new_hosts_needed = list(hostnames)
hosts = (
self.session.query(Host).filter(Host.hostname.in_(hostnames)).all()
)
for host in hosts:
new_hosts_needed.remove(str(host.hostname))

# if we need to create hosts, do them all at once
if new_hosts_needed:
Host.create_many(self.session, new_hosts_needed)
hosts = (
self.session.query(Host).filter(
Host.hostname.in_(hostnames)
).all()
)

if host is None:
host = Host.create(self.session, hostname)
if len(hosts) == 0:
raise exc.BadRequest("No hosts found with given list")

try:
event = Event.create(
self.session, host, user, event_type, note=note
)
if len(hosts) > 1:
# if we are supposed to create many events, we want to do them as a giant batch
events_to_create = []
# we need a unique tx number so we can look these back up again
# FIXME: how can we guarantee uniqueness here?
tx = int(time.time() * 100000) + random.randrange(10000, 99999)
for host in hosts:
events_to_create.append({
"host_id": host.id,
"user": user,
"event_type_id": event_type_id,
"note": note,
"tx": tx
})
Event.create_many(self.session, events_to_create, tx)
else:
# if we are just creating one event, do it the simple way
event = Event.create(
self.session, hosts[0], user, event_type, note=note
)

except IntegrityError as err:
raise exc.Conflict(err.orig.message)
except exc.ValidationError as err:
raise exc.BadRequest(err.message)

self.session.flush()
self.session.commit()

json = event.to_dict(API_VER)
json["href"] = "/api/v1/events/{}".format(event.id)

self.created("/api/v1/events/{}".format(event.id), json)
if len(hosts) == 1:
json = event.to_dict(API_VER)
json["href"] = "/api/v1/events/{}".format(event.id)
self.created(
"/api/v1/events/{}".format(event.id), json
)
else:
# if we created many events, we need to look them up by the TX
# number to figure out what they were since the were created in bulk
created_events = self.session.query(Event).filter(Event.tx == tx).all()
self.created(
data={
"events": (
[event.to_dict(API_VER) for event in created_events]
),
"totalEvents": len(created_events)
}
)

def get(self):
"""**Get all Events**
Expand Down Expand Up @@ -1732,7 +1811,7 @@ def post(self):
if not EMAIL_REGEX.match(creator):
creator += "@" + self.domain
description = self.jbody["description"]
hostnames = self.jbody["hostnames"]
hostnames = self.jbody.get("hostnames") or []

if "targetTime" in self.jbody:
target_time = parser.parse(
Expand Down Expand Up @@ -1765,8 +1844,7 @@ def post(self):
query = self.jbody["hostQuery"]
response = PluginHelper.request_get(params={"query": query})
if response.json()["status"] == "ok":
for hostname in response.json()["results"]:
hostnames.append(hostname)
hostnames.extend(response.json()["results"])

# We need to create a list of hostnames that don't have a Host record
new_hosts_needed = list(hostnames)
Expand Down
14 changes: 11 additions & 3 deletions hermes/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from sqlalchemy.orm import synonym, sessionmaker, Session as _Session, backref
from sqlalchemy.orm import subqueryload
from sqlalchemy.schema import Column, ForeignKey, Index, UniqueConstraint
from sqlalchemy.types import Integer, String, Boolean
from sqlalchemy.types import Integer, String, Boolean, BigInteger
from sqlalchemy.types import DateTime

from .util import slack_message
Expand Down Expand Up @@ -798,7 +798,7 @@ class Event(Model):
)
event_type = relationship(EventType, lazy="joined", backref="events")
note = Column(String(length=1024), nullable=True)
tx = Column(Integer, nullable=True, index=True)
tx = Column(BigInteger, nullable=True, index=True)

__table_args__ = (
Index("event_idx", id, host_id, event_type_id),
Expand Down Expand Up @@ -1193,6 +1193,7 @@ def achieve_many(cls, session, labor_dicts):
(with keys labor and event)
"""
quests = []
all_messages = ""
for labor_dict in labor_dicts:
labor = labor_dict["labor"]
event = labor_dict["event"]
Expand All @@ -1201,7 +1202,7 @@ def achieve_many(cls, session, labor_dicts):
flush=False, commit=False
)

slack_message("*Labor {}* completed.\n\t{}: {} {} => {} {}{}".format(
all_messages += ("*Labor {}* completed.\n\t{}: {} {} => {} {}{}\n".format(
labor.id, labor.host.hostname,
labor.creation_event.event_type.category,
labor.creation_event.event_type.state,
Expand All @@ -1215,6 +1216,13 @@ def achieve_many(cls, session, labor_dicts):
quests.append(labor.quest)
session.flush()
session.commit()

if len(labor_dicts) < 10:
slack_message(all_messages)
else:
slack_message(
"*Labor:* completed {} Labors".format(len(labor_dicts))
)
for quest in quests:
quest.check_for_victory()

Expand Down
2 changes: 1 addition & 1 deletion hermes/version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "0.1.28"
__version__ = "0.1.29"

0 comments on commit c491727

Please sign in to comment.