model

package
v1.3.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 1, 2026 License: Apache-2.0 Imports: 15 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ComponentFilterEqual added in v1.3.1

func ComponentFilterEqual(a, b json.RawMessage) (bool, error)

ComponentFilterEqual reports whether two component_filter JSONB values are semantically equivalent. nil, empty, and the JSON null literal are all treated as equivalent (they all mean "all components"). For type-based filters, element order is ignored.

func CountWaitingTasksForRack

func CountWaitingTasksForRack(
	ctx context.Context,
	idb bun.IDB,
	rackID uuid.UUID,
) (int, error)

CountWaitingTasksForRack returns the count of waiting tasks for a rack.

func ListRacksWithWaitingTasks

func ListRacksWithWaitingTasks(
	ctx context.Context,
	idb bun.IDB,
) ([]uuid.UUID, error)

ListRacksWithWaitingTasks returns the distinct rack IDs that have at least one task in the waiting state.

func MarshalComponentFilter added in v1.3.1

func MarshalComponentFilter(cf *ComponentFilter) (json.RawMessage, error)

MarshalComponentFilter marshals a ComponentFilter to JSON for JSONB storage.

func ReplaceAllDrifts

func ReplaceAllDrifts(ctx context.Context, idb bun.IDB, drifts []ComponentDrift) error

ReplaceAllDrifts replaces all component_drift rows with the given set. This is called once per inventory loop cycle to overwrite stale data.

Types

type BMC

type BMC struct {
	bun.BaseModel `bun:"table:bmc,alias:b"`

	MacAddress  string     `bun:"mac_address,pk"`
	Type        string     `bun:"type,type:varchar(16),default:'Unknown'"`
	ComponentID uuid.UUID  `bun:"component_id,type:uuid,notnull"`
	IPAddress   *string    `bun:"ip_address"`
	User        *string    `bun:"user"`
	Password    *string    `bun:"password"`
	Component   *Component `bun:"rel:belongs-to,join:component_id=id"`
}

func (*BMC) BuildPatch

func (bd *BMC) BuildPatch(cur *BMC) *BMC

BuildPatch builds a patched BMC from the current BMC and the input BMC. It goes through the patchable fields and builds the patched BMC. If there is no change on patchable fields, it returns nil.

func (*BMC) Create

func (bd *BMC) Create(ctx context.Context, tx bun.Tx) error

func (*BMC) InvalidType

func (bd *BMC) InvalidType() bool

InvalidType returns true if the BMC type is unknown.

func (*BMC) Patch

func (bd *BMC) Patch(ctx context.Context, idb bun.IDB) error

type Component

type Component struct {
	bun.BaseModel `bun:"table:component,alias:c"`

	ID              uuid.UUID              `bun:"id,pk,type:uuid,default:gen_random_uuid()"`
	Name            string                 `bun:"name"`
	Type            string                 `bun:"type,type:varchar(16),default:'Compute'"`
	Manufacturer    string                 `bun:"manufacturer,notnull,unique:component_manufacturer_serial_idx"`
	Model           string                 `bun:"model"`
	SerialNumber    string                 `bun:"serial_number,notnull,notnull,unique:component_manufacturer_serial_idx"`
	Description     map[string]any         `bun:"description,type:jsonb,json_use_number"`
	FirmwareVersion string                 `bun:"firmware_version,nullzero"`
	RackID          uuid.UUID              `bun:"rack_id,type:uuid,notnull"`
	SlotID          int                    `bun:"slot_id"`
	TrayIndex       int                    `bun:"tray_index"`
	HostID          int                    `bun:"host_id"`
	IngestedAt      *time.Time             `bun:"ingested_at"`
	DeletedAt       *time.Time             `bun:"deleted_at,soft_delete"`
	Rack            *Rack                  `bun:"rel:belongs-to,join:rack_id=id"`
	BMCs            []BMC                  `bun:"rel:has-many,join:id=component_id"`
	ComponentID     *string                `bun:"external_id"`
	PowerState      *carbideapi.PowerState `bun:"power_state"`
}

func GetAllComponents

func GetAllComponents(ctx context.Context, idb bun.IDB) (ret []Component, err error)

func GetComponentByBMCMAC

func GetComponentByBMCMAC(
	ctx context.Context,
	idb bun.IDB,
	macAddress string,
) (*Component, error)

GetComponentByBMCMAC retrieves a component by its BMC MAC address. Returns the component with all its associated BMCs (needed for powershelf manager queries).

func GetComponentsByType

func GetComponentsByType(ctx context.Context, idb bun.IDB, componentType devicetypes.ComponentType) (ret []Component, err error)

GetComponentsByType returns all components of a specific type with their associated BMCs

func GetListOfComponents

func GetListOfComponents(
	ctx context.Context,
	idb bun.IDB,
	info dbquery.StringQueryInfo,
	manufacturerFilter *dbquery.StringQueryInfo,
	modelFilter *dbquery.StringQueryInfo,
	componentTypes []devicetypes.ComponentType,
	pagination *dbquery.Pagination,
	orderBy *dbquery.OrderBy,
) ([]Component, int32, error)

GetListOfComponents returns a list of components matching the given criteria.

func (*Component) BuildPatch

func (cd *Component) BuildPatch(cur *Component) *Component

BuildPatch builds a patched component from the current component and the input component. It goes through the patchable fields and builds the patched component. If there is no change on patchable fields, it returns nil.

func (*Component) Create

func (cd *Component) Create(ctx context.Context, idb bun.IDB) error

func (*Component) Delete

func (cd *Component) Delete(ctx context.Context, idb bun.IDB) error

Delete soft-deletes the component by setting deleted_at.

func (*Component) ForceDelete added in v1.2.1

func (cd *Component) ForceDelete(ctx context.Context, idb bun.IDB) error

ForceDelete permanently removes the component row from the database.

func (*Component) Get

func (cd *Component) Get(
	ctx context.Context,
	idb bun.IDB,
) (*Component, error)

func (*Component) GetIncludingDeleted added in v1.2.1

func (cd *Component) GetIncludingDeleted(ctx context.Context, idb bun.IDB) (*Component, error)

GetIncludingDeleted retrieves a component by ID regardless of soft-delete status.

func (*Component) InvalidType

func (cd *Component) InvalidType() bool

InvalidType returns true if the component type is unknown.

func (*Component) Patch

func (cd *Component) Patch(ctx context.Context, idb bun.IDB) error

func (*Component) SerialInfo

func (cd *Component) SerialInfo() deviceinfo.SerialInfo

SerialInfo returns the serial number information of the component.

func (*Component) SetComponentIDBySerial

func (cd *Component) SetComponentIDBySerial(ctx context.Context, idb bun.IDB) error

func (*Component) SetFirmwareVersionByComponentID

func (cd *Component) SetFirmwareVersionByComponentID(ctx context.Context, idb bun.IDB) error

func (*Component) SetPowerStateByComponentID

func (cd *Component) SetPowerStateByComponentID(ctx context.Context, idb bun.IDB) error

type ComponentDrift

type ComponentDrift struct {
	bun.BaseModel `bun:"table:component_drift,alias:cd"`

	ID          uuid.UUID   `bun:"id,pk,type:uuid,default:gen_random_uuid()"`
	ComponentID *uuid.UUID  `bun:"component_id,type:uuid"` // NULL for missing_in_expected
	ExternalID  *string     `bun:"external_id"`            // Component ID from the component manager service; NULL for missing_in_actual
	DriftType   DriftType   `bun:"drift_type,type:varchar(32),notnull"`
	Diffs       []FieldDiff `bun:"diffs,type:jsonb,notnull,default:'[]'"`
	CheckedAt   time.Time   `bun:"checked_at,notnull,default:current_timestamp"`
}

ComponentDrift stores validation drift detected by the inventory loop. Each row represents one drift record between expected (component table) and actual (source system) data.

func GetAllDrifts

func GetAllDrifts(ctx context.Context, idb bun.IDB) ([]ComponentDrift, error)

GetAllDrifts retrieves all drift records.

func GetDriftsByComponentIDs

func GetDriftsByComponentIDs(ctx context.Context, idb bun.IDB, componentIDs []uuid.UUID) ([]ComponentDrift, error)

GetDriftsByComponentIDs retrieves drift records for the given component UUIDs.

type ComponentFilter added in v1.3.1

type ComponentFilter struct {
	Kind ComponentFilterKind `json:"kind"`
	// Types lists the component type strings when Kind == "types".
	Types []string `json:"types,omitempty"`
	// Components lists the component UUIDs when Kind == "components".
	Components []uuid.UUID `json:"components,omitempty"`
}

ComponentFilter is the discriminated union stored in component_filter JSONB. Exactly one of Types or Components must be non-nil; Kind is always set.

func UnmarshalComponentFilter added in v1.3.1

func UnmarshalComponentFilter(raw json.RawMessage) (*ComponentFilter, error)

UnmarshalComponentFilter parses a JSONB value into a ComponentFilter. Returns nil if raw is nil, empty, or the JSON null literal — all three representations mean "no filter" (target all components in the rack). The JSON null case arises when bun's AppendJSONValue serialises a nil json.RawMessage for a jsonb-typed column without the nullzero tag.

type ComponentFilterKind added in v1.3.1

type ComponentFilterKind string

ComponentFilterKind discriminates the two variants of ComponentFilter.

const (
	// ComponentFilterKindTypes filters by component type (e.g. COMPUTE, POWERSHELF).
	ComponentFilterKindTypes ComponentFilterKind = "types"
	// ComponentFilterKindComponents targets specific components by their UUIDs.
	ComponentFilterKindComponents ComponentFilterKind = "components"
)

type DriftType

type DriftType string

DriftType represents the type of drift detected for a component.

const (
	// DriftTypeMissingInExpected means the component exists in the source system
	// but is NOT in the local DB (component table).
	DriftTypeMissingInExpected DriftType = "missing_in_expected"

	// DriftTypeMissingInActual means the component exists in the local DB
	// but was NOT found in the source system.
	DriftTypeMissingInActual DriftType = "missing_in_actual"

	// DriftTypeMismatch means the component exists in both the local DB
	// and the source system, but some validation fields have different values.
	DriftTypeMismatch DriftType = "mismatch"
)

type FieldDiff

type FieldDiff struct {
	FieldName     string `json:"field_name"`
	ExpectedValue string `json:"expected_value"`
	ActualValue   string `json:"actual_value"`
}

FieldDiff represents a single field difference between expected and actual values.

type NVLDomain

type NVLDomain struct {
	bun.BaseModel `bun:"table:nvldomain,alias:n"`

	ID        uuid.UUID  `bun:"id,pk,type:uuid,default:gen_random_uuid()"`
	Name      string     `bun:"name,notnull,unique:nvldomain_name_idx"`
	CreatedAt time.Time  `bun:"created_at,nullzero,notnull,default:current_timestamp"`
	DeletedAt *time.Time `bun:"deleted_at,soft_delete"`
	Racks     []Rack     `bun:"rel:has-many,join:id=nvldomain_id"`
}

func GetListOfNVLDomains

func GetListOfNVLDomains(
	ctx context.Context,
	idb bun.IDB,
	info dbquery.StringQueryInfo,
	pagination *dbquery.Pagination,
) ([]NVLDomain, int32, error)

func (*NVLDomain) Create

func (d *NVLDomain) Create(ctx context.Context, idb bun.IDB) error

func (*NVLDomain) Get

func (d *NVLDomain) Get(
	ctx context.Context,
	idb bun.IDB,
) (*NVLDomain, error)

type OperationRule

type OperationRule struct {
	bun.BaseModel `bun:"table:operation_rules,alias:or"`

	ID             uuid.UUID       `bun:"id,pk,type:uuid,default:gen_random_uuid()"`
	Name           string          `bun:"name,notnull"`
	Description    sql.NullString  `bun:"description"`
	OperationType  string          `bun:"operation_type,notnull"`
	OperationCode  string          `bun:"operation_code,notnull"`
	RuleDefinition json.RawMessage `bun:"rule_definition,type:jsonb,notnull"`
	IsDefault      bool            `bun:"is_default,notnull,default:false"`
	CreatedAt      time.Time       `bun:"created_at,notnull,default:current_timestamp"`
	UpdatedAt      time.Time       `bun:"updated_at,notnull,default:current_timestamp"`
}

OperationRule models the persisted operation rule metadata. Rules are templates that define how operations should be performed.

func GetOperationRule

func GetOperationRule(ctx context.Context, idb bun.IDB, id uuid.UUID) (*OperationRule, error)

GetOperationRule retrieves an operation rule by its UUID.

func ListOperationRules

func ListOperationRules(
	ctx context.Context,
	idb bun.IDB,
	options *taskcommon.OperationRuleListOptions,
	pagination *dbquery.Pagination,
) ([]OperationRule, int32, error)

OperationRuleListOptions defines filter options for listing operation rules. ListOperationRules returns all operation rules matching the given criteria with pagination.

func (*OperationRule) Create

func (r *OperationRule) Create(ctx context.Context, idb bun.IDB) error

Create inserts the operation rule record into the backing store.

func (*OperationRule) Delete

func (r *OperationRule) Delete(ctx context.Context, idb bun.IDB) error

Delete deletes the operation rule from the backing store.

func (*OperationRule) Update

func (r *OperationRule) Update(ctx context.Context, idb bun.IDB, columns ...string) error

Update updates specific fields of the operation rule.

type OverlapPolicy added in v1.3.1

type OverlapPolicy string

OverlapPolicy controls what happens when a schedule fires but the previous execution is still active.

const (
	OverlapPolicySkip  OverlapPolicy = "skip"
	OverlapPolicyQueue OverlapPolicy = "queue"
)

type Rack

type Rack struct {
	bun.BaseModel `bun:"table:rack,alias:r"`

	ID           uuid.UUID      `bun:"id,pk,type:uuid,default:gen_random_uuid()"`
	Name         string         `bun:"name,notnull,unique:rack_name_idx"`
	Manufacturer string         `bun:"manufacturer,notnull,unique:rack_manufacturer_serial_idx"`
	SerialNumber string         `bun:"serial_number,notnull,unique:rack_manufacturer_serial_idx"`
	Description  map[string]any `bun:"description,type:jsonb,json_use_number"`
	Location     map[string]any `bun:"location,type:jsonb"`
	NVLDomainID  uuid.UUID      `bun:"nvldomain_id,type:uuid"`
	Status       RackStatus     `bun:"status,type:varchar(16),default:'new'"`
	CreatedAt    time.Time      `bun:"created_at,nullzero,notnull,default:current_timestamp"`
	UpdatedAt    time.Time      `bun:"updated_at,nullzero,notnull,default:current_timestamp"`
	IngestedAt   *time.Time     `bun:"ingested_at"`
	DeletedAt    *time.Time     `bun:"deleted_at,soft_delete"`
	Components   []Component    `bun:"rel:has-many,join:id=rack_id"`
	NVLDomain    *NVLDomain     `bun:"rel:belongs-to,join:nvldomain_id=id"`
}

func GetListOfRacks

func GetListOfRacks(
	ctx context.Context,
	idb bun.IDB,
	info dbquery.StringQueryInfo,
	manufacturerFilter *dbquery.StringQueryInfo,
	modelFilter *dbquery.StringQueryInfo,
	pagination *dbquery.Pagination,
	orderBy *dbquery.OrderBy,
	withComponents bool,
) ([]Rack, int32, error)

func GetRacksByIDs

func GetRacksByIDs(
	ctx context.Context,
	idb bun.IDB,
	ids []uuid.UUID,
	withComponents bool,
) ([]Rack, error)

GetRacksByIDs retrieves multiple racks by their UUIDs

func GetRacksForNVLDomain

func GetRacksForNVLDomain(
	ctx context.Context,
	idb bun.IDB,
	nvlDomainID uuid.UUID,
) ([]Rack, error)

func (*Rack) BuildPatch

func (rd *Rack) BuildPatch(cur *Rack) *Rack

BuildPatch builds a patched rack from the current rack and the input rack. It goes through the patchable fields and builds the patched rack. If there is no change on patchable fields, it returns nil.

func (*Rack) Create

func (rd *Rack) Create(ctx context.Context, idb bun.IDB) error

func (*Rack) Delete added in v1.2.1

func (rd *Rack) Delete(ctx context.Context, idb bun.IDB) error

Delete soft-deletes the rack by setting deleted_at.

func (*Rack) ForceDelete added in v1.2.1

func (rd *Rack) ForceDelete(ctx context.Context, idb bun.IDB) error

ForceDelete permanently removes the rack row from the database. The rack must already be soft-deleted.

func (*Rack) Get

func (rd *Rack) Get(
	ctx context.Context,
	idb bun.IDB,
	withComponents bool,
) (*Rack, error)

func (*Rack) GetIncludingDeleted added in v1.2.1

func (rd *Rack) GetIncludingDeleted(ctx context.Context, idb bun.IDB) (*Rack, error)

GetIncludingDeleted retrieves a rack by ID regardless of soft-delete status.

func (*Rack) Patch

func (rd *Rack) Patch(ctx context.Context, idb bun.IDB) error

func (*Rack) SerialInfo

func (rd *Rack) SerialInfo() deviceinfo.SerialInfo

SerialInfo returns the serial number information of the rack.

type RackRuleAssociation

type RackRuleAssociation struct {
	bun.BaseModel `bun:"table:rack_rule_associations,alias:rra"`

	RackID        uuid.UUID `bun:"rack_id,pk,type:uuid,notnull"`
	OperationType string    `bun:"operation_type,pk,type:varchar(64),notnull"`
	OperationCode string    `bun:"operation_code,pk,type:varchar(64),notnull"`
	RuleID        uuid.UUID `bun:"rule_id,type:uuid,notnull"`
	CreatedAt     time.Time `bun:"created_at,notnull,default:current_timestamp"`
	UpdatedAt     time.Time `bun:"updated_at,notnull,default:current_timestamp"`
}

RackRuleAssociation models the association between a rack and an operation rule. It defines which specific rule a rack should use for a given operation type.

func GetRackRuleAssociation

func GetRackRuleAssociation(
	ctx context.Context,
	idb bun.IDB,
	rackID uuid.UUID,
	opType taskcommon.TaskType,
	operationCode string,
) (*RackRuleAssociation, error)

GetRackRuleAssociation retrieves the rule association for a rack, operation type, and operation code.

func ListRackRuleAssociations

func ListRackRuleAssociations(
	ctx context.Context,
	idb bun.IDB,
	rackID uuid.UUID,
) ([]RackRuleAssociation, error)

ListRackRuleAssociations retrieves all rule associations for a rack.

func (*RackRuleAssociation) Create

func (a *RackRuleAssociation) Create(ctx context.Context, idb bun.IDB) error

Create inserts the rack rule association into the backing store.

func (*RackRuleAssociation) Delete

func (a *RackRuleAssociation) Delete(ctx context.Context, idb bun.IDB) error

Delete removes the rack rule association from the backing store.

type RackStatus

type RackStatus string
const (
	RackStatusNew       RackStatus = "new"
	RackStatusIngesting RackStatus = "ingesting"
	RackStatusIngested  RackStatus = "ingested"
)

type SpecType added in v1.3.1

type SpecType string

SpecType identifies the scheduling mechanism for a task schedule.

const (
	SpecTypeInterval SpecType = "interval"
	SpecTypeCron     SpecType = "cron"
	SpecTypeOneTime  SpecType = "one-time"
)

type Task

type Task struct {
	bun.BaseModel `bun:"table:task,alias:t"`

	ID            uuid.UUID                 `bun:"id,pk,type:uuid,default:gen_random_uuid()"`
	Type          taskcommon.TaskType       `bun:"type,type:varchar(64),notnull"`
	ExecutorType  taskcommon.ExecutorType   `bun:"executor_type,type:varchar(64),nullzero"`
	Information   json.RawMessage           `bun:"information,type:jsonb,json_use_number"`
	Description   string                    `bun:"description,nullzero"`
	RackID        uuid.UUID                 `bun:"rack_id,type:uuid,notnull"` // The rack this task operates on
	Attributes    taskcommon.TaskAttributes `bun:"attributes,type:jsonb"`
	ExecutionID   string                    `bun:"execution_id,notnull"`
	Status        taskcommon.TaskStatus     `bun:"status,type:varchar(32),notnull"`
	Message       string                    `bun:"message,nullzero"`
	AppliedRuleID *uuid.UUID                `bun:"applied_rule_id,type:uuid"` // Which operation rule was applied
	CreatedAt     time.Time                 `bun:"created_at,nullzero,notnull,default:current_timestamp"`
	UpdatedAt     time.Time                 `bun:"updated_at,nullzero,notnull,default:current_timestamp"`
	StartedAt     *time.Time                `bun:"started_at"`
	FinishedAt    *time.Time                `bun:"finished_at"`

	// QueueExpiresAt is set only for waiting tasks. After this time, the
	// Promoter will discard the task instead of promoting it.
	QueueExpiresAt *time.Time `bun:"queue_expires_at"`
}

Task models the persisted task metadata managed by RLA.

func GetTask

func GetTask(ctx context.Context, idb bun.IDB, id uuid.UUID) (*Task, error)

GetTask retrieves the task by its UUID.

func ListTasks

func ListTasks(
	ctx context.Context,
	idb bun.IDB,
	options *taskcommon.TaskListOptions,
	pagination *dbquery.Pagination,
) ([]Task, int32, error)

ListTasks returns all tasks that match the given criteria.

func ListTasksForRackByStatus

func ListTasksForRackByStatus(
	ctx context.Context,
	idb bun.IDB,
	rackID uuid.UUID,
	statuses []taskcommon.TaskStatus,
) ([]Task, error)

ListTasksForRackByStatus returns tasks for a rack matching any of the given statuses, ordered oldest-first.

func (*Task) Create

func (t *Task) Create(ctx context.Context, idb bun.IDB) error

Create inserts the task record into the backing store.

func (*Task) UpdateScheduledTask

func (t *Task) UpdateScheduledTask(
	ctx context.Context,
	idb bun.IDB,
) error

UpdateScheduledTask updates the scheduled task information.

func (*Task) UpdateTaskStatus

func (t *Task) UpdateTaskStatus(
	ctx context.Context,
	idb bun.IDB,
	status taskcommon.TaskStatus,
	message string,
) error

UpdateTaskStatus updates the status of the task.

type TaskSchedule added in v1.3.1

type TaskSchedule struct {
	bun.BaseModel `bun:"table:task_schedule,alias:ts"`

	ID                uuid.UUID       `bun:"id,pk,type:uuid,default:gen_random_uuid()"`
	Name              string          `bun:"name,notnull"`
	SpecType          SpecType        `bun:"spec_type,type:varchar(16),notnull"`
	Spec              string          `bun:"spec,notnull"`
	Timezone          string          `bun:"timezone,notnull"`
	OperationTemplate json.RawMessage `bun:"operation_template,type:jsonb,notnull"`
	OverlapPolicy     OverlapPolicy   `bun:"overlap_policy,type:varchar(16),notnull"`
	Enabled           bool            `bun:"enabled,notnull"`
	NextRunAt         *time.Time      `bun:"next_run_at"`
	LastRunAt         *time.Time      `bun:"last_run_at"`
	CreatedAt         time.Time       `bun:"created_at,notnull,default:current_timestamp"`
	UpdatedAt         time.Time       `bun:"updated_at,notnull,default:current_timestamp"`
}

TaskSchedule is the bun model for the task_schedule table.

type TaskScheduleScope added in v1.3.1

type TaskScheduleScope struct {
	bun.BaseModel `bun:"table:task_schedule_scope,alias:tss"`

	ID              uuid.UUID       `bun:"id,pk,type:uuid,default:gen_random_uuid()"`
	ScheduleID      uuid.UUID       `bun:"schedule_id,type:uuid,notnull"`
	RackID          uuid.UUID       `bun:"rack_id,type:uuid,notnull"`
	ComponentFilter json.RawMessage `bun:"component_filter,type:jsonb,nullzero"`
	LastTaskID      *uuid.UUID      `bun:"last_task_id,type:uuid"`
	CreatedAt       time.Time       `bun:"created_at,notnull,default:current_timestamp"`
}

TaskScheduleScope is the bun model for the task_schedule_scope table. Each row represents one rack target in a schedule's scope. LastTaskID tracks the task produced for this rack by the most recent firing, used by the overlap check to determine whether the previous execution is still active.

Invariant: when ComponentFilter has kind "components", every UUID listed in that filter must belong to RackID. This is enforced by the API write path (resolveComponentScope groups components by rack before persisting). At fire time the dispatcher sets RequiredRackID on the SubmitTask request, so any violation caused by a direct DB modification (e.g. a component moved to a different rack) is detected before any task is created, and the scope is skipped with an error.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL