"""endpoints plugin baseline (real create). A NEW plugin: the core chain never knew about endpointstates or endpointevents, so this per-plugin chain is their sole authoritative creator. Emits explicit Alembic ops rather than using the create_plugin_tables helper: the helper builds a per-plugin MetaData filtered to the plugin's own tables, so the foreign key to the core assets table cannot resolve at CreateTable-compile time (NoReferencedTableError). Tables inherit the connection's default charset, matching how the core chain creates its own. """ from alembic import op from shopdb.plugins.alembic_template import ( create_index_if_missing, create_table_if_missing, ) import sqlalchemy as sa # revision identifiers, used by Alembic. revision = 'endpoints0001baseline' down_revision = None branch_labels = None depends_on = None def upgrade(): create_table_if_missing( 'endpointstates', sa.Column('endpointstateid', sa.Integer(), nullable=False), sa.Column('assetid', sa.Integer(), nullable=False), sa.Column('hostname', sa.String(length=100), nullable=True), sa.Column('loggedinuser', sa.String(length=100), nullable=True), sa.Column('agentversion', sa.String(length=40), nullable=True), sa.Column('lastboottime', sa.DateTime(), nullable=True), sa.Column('uptimeseconds', sa.BigInteger(), nullable=True), sa.Column('idleseconds', sa.BigInteger(), nullable=True), sa.Column('cpupercent', sa.Float(), nullable=True), sa.Column('memorypercent', sa.Float(), nullable=True), sa.Column('servicesjson', sa.Text(length=16777215), nullable=True), sa.Column('portsjson', sa.Text(length=16777215), nullable=True), sa.Column('interfacesjson', sa.Text(length=16777215), nullable=True), sa.Column('scheduledtasksjson', sa.Text(length=16777215), nullable=True), sa.Column('lastreportedat', sa.DateTime(), nullable=True), sa.Column('createdat', sa.DateTime(), nullable=False), sa.ForeignKeyConstraint(['assetid'], ['assets.assetid'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('endpointstateid'), sa.UniqueConstraint('assetid'), ) create_index_if_missing('ix_endpointstates_assetid', 'endpointstates', ['assetid']) create_index_if_missing('ix_endpointstates_lastreportedat', 'endpointstates', ['lastreportedat']) create_table_if_missing( 'endpointevents', sa.Column('endpointeventid', sa.Integer(), nullable=False), sa.Column('assetid', sa.Integer(), nullable=False), sa.Column('eventid', sa.Integer(), nullable=False), sa.Column('eventchannel', sa.String(length=50), nullable=True), sa.Column('eventsource', sa.String(length=255), nullable=True), sa.Column('eventtime', sa.DateTime(), nullable=True), sa.Column('eventmessage', sa.Text(), nullable=True), sa.Column('payloadjson', sa.Text(length=16777215), nullable=True), sa.Column('recordedat', sa.DateTime(), nullable=False), sa.ForeignKeyConstraint(['assetid'], ['assets.assetid'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('endpointeventid'), ) create_index_if_missing('ix_endpointevents_assetid', 'endpointevents', ['assetid']) create_index_if_missing('ix_endpointevents_eventid', 'endpointevents', ['eventid']) create_index_if_missing('ix_endpointevents_eventtime', 'endpointevents', ['eventtime']) # Alerting/history reads by "this endpoint's events over time", so the # composite is the index that actually gets used. create_index_if_missing('idx_endpointevents_assetevent', 'endpointevents', ['assetid', 'eventtime']) def downgrade(): op.drop_index('idx_endpointevents_assetevent', table_name='endpointevents') op.drop_index('ix_endpointevents_eventtime', table_name='endpointevents') op.drop_index('ix_endpointevents_eventid', table_name='endpointevents') op.drop_index('ix_endpointevents_assetid', table_name='endpointevents') op.drop_table('endpointevents') op.drop_index('ix_endpointstates_lastreportedat', table_name='endpointstates') op.drop_index('ix_endpointstates_assetid', table_name='endpointstates') op.drop_table('endpointstates')